Files.txt 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356
  1. MODULE HostFiles;
  2. (* THIS IS TEXT COPY OF Files.odc *)
  3. (* DO NOT EDIT *)
  4. IMPORT SYSTEM, Kernel, Files, LinLibc;
  5. CONST
  6. tempName = "odcxxxxx";
  7. docType = "odc";
  8. serverVersion = TRUE;
  9. pathLen* = 260;
  10. nofbufs = 4; (* max number of buffers per file *)
  11. bufsize = 2 * 1024; (* size of each buffer *)
  12. invalid = LinLibc.NULL;
  13. temp = 0; new = 1; shared = 2; hidden = 3; exclusive = 4; closed = 5; (* file states *)
  14. create = -1;
  15. ok = 0;
  16. invalidName = 1;
  17. invalidNameErr = MAX(INTEGER); (* On Windows this is 123 *)
  18. notFound = 2;
  19. fileNotFoundErr = LinLibc.ENOENT;
  20. pathNotFoundErr = LinLibc.ENOENT;
  21. existsAlready = 3;
  22. fileExistsErr = LinLibc.EEXIST;
  23. alreadyExistsErr = LinLibc.EEXIST; (* is alreadyExistsErr needed? *)
  24. writeProtected = 4;
  25. writeProtectedErr = LinLibc.EACCES;
  26. ioError = 5; (* same as LinLibc.EIO *)
  27. accessDenied = 6;
  28. accessDeniedErr = LinLibc.EACCES;
  29. sharingErr = LinLibc.EACCES;
  30. netAccessDeniedErr = LinLibc.EACCES;
  31. notEnoughMem = 80;
  32. notEnoughMemoryErr = LinLibc.ENOMEM;
  33. notEnoughDisk = 81;
  34. diskFullErr = LinLibc.EDQUOT;
  35. tooManyOpenFilesErr = LinLibc.EMFILE;
  36. noMoreFilesErr = 18;
  37. cancel = -8; retry = -9;
  38. TYPE
  39. FullName* = ARRAY pathLen OF CHAR;
  40. Locator* = POINTER TO RECORD (Files.Locator)
  41. path-: FullName; (* without trailing "/" *)
  42. maxLen-: INTEGER; (* maximum name length *)
  43. caseSens-: BOOLEAN; (* case sensitive file compares *)
  44. rootLen-: INTEGER (* for network version *)
  45. END;
  46. Buffer = POINTER TO RECORD
  47. dirty: BOOLEAN;
  48. org, len: INTEGER;
  49. data: ARRAY bufsize OF BYTE
  50. END;
  51. File = POINTER TO RECORD (Files.File)
  52. state: INTEGER;
  53. name: FullName;
  54. ref: LinLibc.PtrFILE;
  55. loc: Locator;
  56. swapper: INTEGER; (* index into file table / next buffer to swap *)
  57. len: INTEGER;
  58. bufs: ARRAY nofbufs OF Buffer;
  59. t: LONGINT (* time stamp of last file operation *)
  60. END;
  61. Reader = POINTER TO RECORD (Files.Reader)
  62. base: File;
  63. org, offset: INTEGER;
  64. buf: Buffer
  65. END;
  66. Writer = POINTER TO RECORD (Files.Writer)
  67. base: File;
  68. org, offset: INTEGER;
  69. buf: Buffer
  70. END;
  71. Directory = POINTER TO RECORD (Files.Directory)
  72. temp, startup: Locator
  73. END;
  74. Identifier = RECORD (Kernel.Identifier)
  75. name: FullName
  76. END;
  77. Searcher = RECORD (Kernel.Identifier)
  78. t0: INTEGER;
  79. f: File
  80. END;
  81. Counter = RECORD (Kernel.Identifier)
  82. count: INTEGER
  83. END;
  84. ShortName = ARRAY pathLen OF SHORTCHAR;
  85. VAR
  86. MapParamString*: PROCEDURE(in, p0, p1, p2: ARRAY OF CHAR; OUT out: ARRAY OF CHAR);
  87. dir: Directory;
  88. wildcard: Files.Type;
  89. startupDir: FullName;
  90. startupLen: INTEGER;
  91. res: INTEGER;
  92. (* debugging functions *)
  93. PROCEDURE Msg (IN str: ARRAY OF CHAR);
  94. VAR ss: ARRAY 1024 OF SHORTCHAR; res, l: INTEGER;
  95. BEGIN
  96. ss := SHORT(str);
  97. l := LEN(ss$);
  98. ss[l] := 0AX; ss[l + 1] := 0X;
  99. res := LinLibc.printf(ss);
  100. res := LinLibc.fflush(0)
  101. END Msg;
  102. PROCEDURE Int (x: LONGINT);
  103. VAR j, k: INTEGER; ch: CHAR; a, s: ARRAY 32 OF CHAR;
  104. BEGIN
  105. IF x # MIN(LONGINT) THEN
  106. IF x < 0 THEN s[0] := "-"; k := 1; x := -x ELSE k := 0 END;
  107. j := 0; REPEAT a[j] := CHR(x MOD 10 + ORD("0")); x := x DIV 10; INC(j) UNTIL x = 0
  108. ELSE
  109. a := "8085774586302733229"; s[0] := "-"; k := 1;
  110. j := 0; WHILE a[j] # 0X DO INC(j) END
  111. END;
  112. ASSERT(k + j < LEN(s), 20);
  113. REPEAT DEC(j); ch := a[j]; s[k] := ch; INC(k) UNTIL j = 0;
  114. s[k] := 0X;
  115. Msg(s);
  116. END Int;
  117. (* end of debugging functions *)
  118. (* get error num from linux *)
  119. PROCEDURE LinLibc_errno (): INTEGER;
  120. VAR
  121. addr, errno: INTEGER;
  122. BEGIN
  123. addr := LinLibc.__errno_location();
  124. SYSTEM.GET(addr, errno);
  125. RETURN errno
  126. END LinLibc_errno;
  127. PROCEDURE Error (n: INTEGER): INTEGER;
  128. VAR res: INTEGER;
  129. BEGIN
  130. IF n = ok THEN res := ok
  131. ELSIF n = invalidNameErr THEN res := invalidName
  132. ELSIF (n = fileNotFoundErr) OR (n = pathNotFoundErr) THEN res := notFound
  133. ELSIF (n = fileExistsErr) OR (n = alreadyExistsErr) THEN res := existsAlready
  134. ELSIF n = writeProtectedErr THEN res := writeProtected
  135. ELSIF (n = sharingErr) OR (n = accessDeniedErr) OR (n = netAccessDeniedErr) THEN res := accessDenied
  136. ELSIF n = notEnoughMemoryErr THEN res := notEnoughMem
  137. ELSIF (n = diskFullErr) OR (n = tooManyOpenFilesErr) THEN res := notEnoughDisk
  138. ELSE res := -n
  139. END;
  140. RETURN res
  141. END Error;
  142. PROCEDURE Diff (IN a, b: ARRAY OF CHAR; caseSens: BOOLEAN): INTEGER;
  143. VAR i: INTEGER; cha, chb: CHAR;
  144. BEGIN
  145. i := 0;
  146. REPEAT
  147. cha := a[i]; chb := b[i]; INC(i);
  148. IF cha # chb THEN
  149. IF ~caseSens THEN
  150. IF (cha >= "a") & ((cha <= "z") OR (cha >= 0E0X) & (cha <= 0FEX) & (cha # 0F7X)) THEN
  151. cha := CAP(cha)
  152. END;
  153. IF (chb >= "a") & ((chb <= "z") OR (chb >= 0E0X) & (chb <= 0FEX) & (chb # 0F7X)) THEN
  154. chb := CAP(chb)
  155. END
  156. END;
  157. IF cha = "\" THEN cha := "/" END;
  158. IF chb = "\" THEN chb := "/" END;
  159. IF cha # chb THEN RETURN ORD(cha) - ORD(chb) END
  160. END
  161. UNTIL cha = 0X;
  162. RETURN 0
  163. END Diff;
  164. PROCEDURE Stat (IN fname: FullName; VAR buf: LinLibc.stat_t; OUT res: INTEGER);
  165. VAR s: ShortName;
  166. BEGIN
  167. s := SHORT(fname);
  168. res := LinLibc.stat(s, buf); (* Shiryaev A. V.: OpenBSD *)
  169. END Stat;
  170. PROCEDURE ModeToAttr (mode: SET; OUT attr: SET; OUT isDir: BOOLEAN);
  171. CONST read = 8; write = 7; execute = 6; file = 15; (* bits for permissions for the current user (see man chmod) *)
  172. BEGIN
  173. attr := {};
  174. IF ~(write IN mode) THEN INCL(attr, Files.readOnly) END;
  175. isDir := ~(file IN mode) (* see "man 2 stat" for details *)
  176. END ModeToAttr;
  177. PROCEDURE NewLocator* (IN fname: ARRAY OF CHAR): Locator;
  178. VAR loc: Locator; i: INTEGER;
  179. BEGIN
  180. NEW(loc); loc.path := fname$; i := 0;
  181. WHILE loc.path[i] # 0X DO INC(i) END;
  182. IF (loc.path[i-1] = "/") OR (loc.path[i-1] = "\") THEN loc.path[i-1] := 0X END;
  183. loc.maxLen := LinLibc.NAME_MAX; loc.caseSens := TRUE;
  184. RETURN loc
  185. END NewLocator;
  186. PROCEDURE GetType (IN name: ARRAY OF CHAR; VAR type: Files.Type);
  187. VAR i, j: INTEGER; ch: CHAR;
  188. BEGIN
  189. i := 0; j := 0;
  190. WHILE name[i] # 0X DO INC(i) END;
  191. WHILE (i > 0) & (name[i] # ".") DO DEC(i) END;
  192. IF i > 0 THEN
  193. INC(i); ch := name[i];
  194. WHILE (j < LEN(type) - 1) & (ch # 0X) DO
  195. IF (ch >= "A") & (ch <= "Z") THEN ch := CHR(ORD(ch) + (ORD("a") - ORD("A"))) END;
  196. type[j] := ch; INC(j);
  197. INC(i); ch := name[i]
  198. END
  199. END;
  200. type[j] := 0X
  201. END GetType;
  202. PROCEDURE Append (IN path, name: ARRAY OF CHAR; type: Files.Type; max: INTEGER;
  203. VAR res: ARRAY OF CHAR
  204. );
  205. VAR i, j, n, m, dot: INTEGER; ch: CHAR;
  206. BEGIN
  207. i := 0;
  208. WHILE path[i] # 0X DO res[i] := path[i]; INC(i) END;
  209. IF path # "" THEN
  210. ASSERT((res[i-1] # "/") & (res[i-1] # "\"), 100);
  211. res[i] := "/"; INC(i)
  212. END;
  213. j := 0; ch := name[0]; n := 0; m := max; dot := -1;
  214. IF max = 12 THEN m := 8 END;
  215. WHILE (i < LEN(res) - 1) & (ch # 0X) DO
  216. IF (ch = "/") OR (ch = "\") THEN
  217. res[i] := ch; INC(i); n := 0; m := max; dot := -1;
  218. IF max = 12 THEN m := 8 END
  219. ELSIF (n < m) OR (ch = ".") & (n = 8) THEN
  220. res[i] := ch; INC(i); INC(n);
  221. IF ch = "." THEN dot := n;
  222. IF max = 12 THEN m := n + 3 END
  223. END
  224. END;
  225. INC(j); ch := name[j]
  226. END;
  227. IF (dot = -1) & (type # "") THEN
  228. IF max = 12 THEN m := n + 4 END;
  229. IF (n < m) & (i < LEN(res) - 1) THEN res[i] := "."; INC(i); INC(n); dot := n END
  230. END;
  231. IF n = dot THEN j := 0;
  232. WHILE (n < m) & (i < LEN(res) - 1) & (type[j] # 0X) DO res[i] := type[j]; INC(i); INC(j) END
  233. END;
  234. res[i] := 0X
  235. END Append;
  236. PROCEDURE CloseFileHandle (f: File; VAR res: INTEGER);
  237. BEGIN
  238. IF (f.ref = invalid) OR (LinLibc.fclose(f.ref) = 0) THEN res := ok (* !!! *)
  239. ELSE res := LinLibc_errno()
  240. END;
  241. f.ref := invalid
  242. END CloseFileHandle;
  243. PROCEDURE CloseFile (f: File; VAR res: INTEGER);
  244. VAR s: INTEGER; n: ShortName;
  245. BEGIN
  246. IF f.state = exclusive THEN
  247. f.Flush;
  248. res := LinLibc.fflush(f.ref)
  249. END;
  250. s := f.state; f.state := closed;
  251. CloseFileHandle (f, res);
  252. IF (s IN {temp, new, hidden}) & (f.name # "") THEN
  253. n := SHORT(f.name$);
  254. res := LinLibc.remove(n)
  255. END
  256. END CloseFile;
  257. PROCEDURE (f: File) FINALIZE;
  258. VAR res: INTEGER;
  259. BEGIN
  260. IF f.state # closed THEN CloseFile(f, res) END
  261. END FINALIZE;
  262. PROCEDURE (VAR id: Identifier) Identified (): BOOLEAN;
  263. VAR f: File;
  264. BEGIN
  265. f := id.obj(File);
  266. RETURN (f.state IN {shared, exclusive}) & (Diff(f.name, id.name, f.loc.caseSens) = 0)
  267. END Identified;
  268. PROCEDURE ThisFile (IN name: FullName): File;
  269. VAR id: Identifier; p: ANYPTR;
  270. BEGIN
  271. id.typ := SYSTEM.TYP(File); id.name := name$;
  272. p := Kernel.ThisFinObj(id);
  273. IF p # NIL THEN RETURN p(File)
  274. ELSE RETURN NIL
  275. END
  276. END ThisFile;
  277. PROCEDURE (VAR s: Searcher) Identified (): BOOLEAN;
  278. VAR f: File;
  279. BEGIN
  280. f := s.obj(File);
  281. IF (f.ref # invalid) & ((s.f = NIL) OR (f.t < s.f.t)) THEN s.f := f END;
  282. RETURN FALSE
  283. END Identified;
  284. PROCEDURE SearchFileToClose;
  285. VAR s: Searcher; p: ANYPTR; (* res: LONGINT; *)
  286. BEGIN
  287. s.typ := SYSTEM.TYP(File); s.f := NIL;
  288. p := Kernel.ThisFinObj(s);
  289. IF s.f # NIL THEN
  290. res := LinLibc.fclose(s.f.ref); s.f.ref := invalid;
  291. IF res = 0 THEN res := LinLibc_errno(); HALT(100) END
  292. END
  293. END SearchFileToClose;
  294. PROCEDURE ExistingFile (VAR n: ShortName): BOOLEAN;
  295. VAR f: LinLibc.PtrFILE; ret: BOOLEAN; res: INTEGER;
  296. BEGIN
  297. f := LinLibc.fopen(n, "r");
  298. IF f # LinLibc.NULL THEN
  299. res := LinLibc.fclose(f);
  300. ret := TRUE
  301. ELSE
  302. ret := FALSE
  303. END;
  304. RETURN ret
  305. END ExistingFile;
  306. PROCEDURE MoveFile (VAR old, new: ShortName; VAR res: INTEGER); (* as the WinApi.MoveFile *)
  307. BEGIN
  308. IF ExistingFile(new) THEN
  309. res := fileExistsErr
  310. ELSE
  311. IF LinLibc.rename(old, new) = 0 THEN res := ok
  312. ELSE res := LinLibc_errno();
  313. END
  314. END
  315. END MoveFile;
  316. PROCEDURE NewFileRef (state: INTEGER; VAR name: FullName; VAR ref, res: INTEGER);
  317. VAR n: ShortName;
  318. BEGIN
  319. n := SHORT(name$);
  320. IF state = create THEN (* Create should fail if file already exists *)
  321. IF ExistingFile(n) THEN
  322. ref := invalid; res := fileExistsErr
  323. ELSE
  324. ref := LinLibc.fopen(n, "w+");
  325. IF ref = invalid THEN res := LinLibc_errno() ELSE res := ok END
  326. END
  327. ELSIF state = shared THEN
  328. ref := LinLibc.fopen(n, "r");
  329. IF ref = invalid THEN res := LinLibc_errno() ELSE res := ok END
  330. ELSE
  331. ref := LinLibc.fopen(n, "r+");
  332. IF ref = invalid THEN res := LinLibc_errno() ELSE res := ok END
  333. END
  334. END NewFileRef;
  335. PROCEDURE OpenFile (state: INTEGER; VAR name: FullName; VAR ref, res: INTEGER);
  336. BEGIN
  337. NewFileRef(state, name, ref, res);
  338. IF ref = invalid THEN
  339. IF res = tooManyOpenFilesErr THEN
  340. Kernel.Collect;
  341. NewFileRef(state, name, ref, res);
  342. IF ref = invalid THEN
  343. res := LinLibc_errno();
  344. IF res = tooManyOpenFilesErr THEN
  345. SearchFileToClose;
  346. NewFileRef(state, name, ref, res);
  347. END
  348. ELSE res := ok
  349. END
  350. END
  351. ELSE res := ok
  352. END
  353. END OpenFile;
  354. PROCEDURE GetTempFileName (IN path: FullName; OUT name: FullName; num: INTEGER);
  355. VAR i: INTEGER; str: ARRAY 16 OF CHAR;
  356. BEGIN
  357. str := tempName; i := 7;
  358. WHILE i > 2 DO
  359. str[i] := CHR(num MOD 10 + ORD("0")); DEC(i); num := num DIV 10
  360. END;
  361. Append(path, str, "", 8, name)
  362. END GetTempFileName;
  363. PROCEDURE CreateFile (f: File; VAR res: INTEGER);
  364. VAR num, n: INTEGER;
  365. BEGIN
  366. IF f.name = "" THEN
  367. num := LinLibc.clock(); n := 200;
  368. REPEAT
  369. GetTempFileName(f.loc.path, f.name, num); INC(num); DEC(n);
  370. OpenFile(create, f.name, f.ref, res)
  371. UNTIL (res # fileExistsErr) OR (n = 0)
  372. ELSE
  373. OpenFile(f.state, f.name, f.ref, res)
  374. END
  375. END CreateFile;
  376. PROCEDURE Delete (IN fname, path: FullName; VAR res: INTEGER);
  377. VAR num, n: INTEGER; f: File; new: FullName; attr: SET; fn, nn: ShortName; buf: LinLibc.stat_t; isDir: BOOLEAN;
  378. BEGIN
  379. ASSERT(fname # "", 100);
  380. f := ThisFile(fname); fn := SHORT(fname$);
  381. IF f = NIL THEN
  382. IF LinLibc.remove(fn) = 0 THEN
  383. res := ok
  384. ELSE
  385. res := LinLibc.fflush(0);
  386. IF LinLibc.remove(fn) = 0 THEN res := ok ELSE res := LinLibc_errno() END
  387. END
  388. ELSE (* still in use => make it anonymous *)
  389. IF f.ref # invalid THEN res := LinLibc.fclose(f.ref); f.ref := invalid END; (* !!! *)
  390. Stat(f.name, buf, res);
  391. ModeToAttr(buf.st_mode, attr, isDir);
  392. IF (res = ok) & ~(Files.readOnly IN attr) THEN
  393. num := LinLibc.clock(); n := 200;
  394. REPEAT
  395. GetTempFileName(path, new, num); INC(num); DEC(n);
  396. nn := SHORT(new$);
  397. MoveFile(fn, nn, res);
  398. UNTIL (res # fileExistsErr) OR (n = 0);
  399. IF res = ok THEN
  400. f.state := hidden; f.name := new$
  401. END
  402. ELSE
  403. res := writeProtectedErr
  404. END
  405. END
  406. END Delete;
  407. PROCEDURE FlushBuffer (f: File; i: INTEGER);
  408. VAR buf: Buffer; res: INTEGER;
  409. BEGIN
  410. buf := f.bufs[i];
  411. IF (buf # NIL) & buf.dirty THEN
  412. IF f.ref = invalid THEN CreateFile(f, res) (* ASSERT(res = ok, 100) *) END;
  413. IF f.ref # invalid THEN
  414. res := LinLibc.fseek(f.ref, buf.org, LinLibc.SEEK_SET);
  415. IF LinLibc.fwrite(SYSTEM.ADR(buf.data), 1, buf.len, f.ref) < buf.len THEN
  416. res := LinLibc_errno(); HALT(101)
  417. END;
  418. res := LinLibc.fflush(f.ref);
  419. buf.dirty := FALSE; f.t := Kernel.Time()
  420. END
  421. END
  422. END FlushBuffer;
  423. (* File *)
  424. PROCEDURE (f: File) NewReader (old: Files.Reader): Files.Reader;
  425. VAR r: Reader;
  426. BEGIN (* portable *)
  427. ASSERT(f.state # closed, 20);
  428. IF (old # NIL) & (old IS Reader) THEN r := old(Reader) ELSE NEW(r) END;
  429. IF r.base # f THEN
  430. r.base := f; r.buf := NIL; r.SetPos(0)
  431. END;
  432. r.eof := FALSE;
  433. RETURN r
  434. END NewReader;
  435. PROCEDURE (f: File) NewWriter (old: Files.Writer): Files.Writer;
  436. VAR w: Writer;
  437. BEGIN (* portable *)
  438. ASSERT(f.state # closed, 20); ASSERT(f.state # shared, 21);
  439. IF (old # NIL) & (old IS Writer) THEN w := old(Writer) ELSE NEW(w) END;
  440. IF w.base # f THEN
  441. w.base := f; w.buf := NIL; w.SetPos(f.len)
  442. END;
  443. RETURN w
  444. END NewWriter;
  445. PROCEDURE (f: File) Length (): INTEGER;
  446. BEGIN (* portable *)
  447. RETURN f.len
  448. END Length;
  449. PROCEDURE (f: File) Flush;
  450. VAR i: INTEGER;
  451. BEGIN (* portable *)
  452. i := 0; WHILE i # nofbufs DO FlushBuffer(f, i); INC(i) END
  453. END Flush;
  454. PROCEDURE GetPath (IN fname: FullName; OUT path: FullName);
  455. VAR i: INTEGER;
  456. BEGIN
  457. path := fname$; i := LEN(path$);
  458. WHILE (i > 0) & (path[i] # "\") & (path[i] # "/") & (path[i-1] # ":") DO DEC(i) END;
  459. path[i] := 0X
  460. END GetPath;
  461. PROCEDURE CreateDir (VAR path: FullName; VAR res: INTEGER);
  462. VAR (*sec: KERNEL32.SecurityAttributes;*) p: FullName; s: ShortName;
  463. BEGIN
  464. ASSERT(path # "", 100);
  465. s := SHORT(path$);
  466. res := LinLibc.mkdir(s, {0..MAX(SET)}); (* full rights are masked with the umask, is this correct? *)
  467. IF res # ok THEN
  468. res := LinLibc_errno();
  469. IF (res = LinLibc.ENOTDIR) OR (res = LinLibc.ENOENT) THEN
  470. GetPath(path, p);
  471. CreateDir(p, res); (* recursive call *)
  472. IF res = ok THEN
  473. res := LinLibc.mkdir(s, {0..MAX(SET)}); (* full rights are masked with the umask, is this correct? *)
  474. IF res # ok THEN res := LinLibc_errno() END
  475. END
  476. END
  477. END
  478. END CreateDir;
  479. PROCEDURE CheckPath (VAR path: FullName; ask: BOOLEAN; VAR res: INTEGER);
  480. VAR s: ARRAY 300 OF CHAR; t: ARRAY 32 OF CHAR;
  481. BEGIN
  482. (*IF ask THEN
  483. IF MapParamString # NIL THEN
  484. MapParamString("#Host:CreateDir", path, "", "", s);
  485. MapParamString("#Host:MissingDirectory", "", "", "", t)
  486. ELSE
  487. s := path$; t := "Missing Directory"
  488. END;
  489. res := Kernel.MessageBox(t, s, {Kernel.mbOk, Kernel.mbCancel})
  490. ELSE
  491. res := Kernel.mbOk
  492. END;*)
  493. (*IF res = Kernel.mbOk THEN*) CreateDir(path, res)
  494. (*ELSIF res = Kernel.mbCancel THEN res := cancel
  495. END*)
  496. END CheckPath;
  497. PROCEDURE CheckDelete (IN fname, path: FullName; ask: BOOLEAN; VAR res: INTEGER);
  498. VAR s: ARRAY 300 OF CHAR; t: ARRAY 16 OF CHAR;
  499. BEGIN
  500. REPEAT
  501. Delete(fname, path, res);
  502. IF (res = writeProtectedErr)
  503. OR (res = sharingErr)
  504. OR (res = accessDeniedErr)
  505. OR (res = netAccessDeniedErr)
  506. THEN
  507. (*IF ask THEN
  508. IF MapParamString # NIL THEN
  509. IF res = writeProtectedErr THEN
  510. MapParamString("#Host:ReplaceWriteProtected", fname, 0DX, "", s)
  511. ELSIF (res = accessDeniedErr) OR (res = netAccessDeniedErr) THEN
  512. MapParamString("#Host:ReplaceAccessDenied", fname, 0DX, "", s)
  513. ELSE
  514. MapParamString("#Host:ReplaceInUse", fname, 0DX, "", s)
  515. END;
  516. MapParamString("#Host:FileError", "", "", "", t)
  517. ELSE
  518. s := fname$; t := "File Error"
  519. END;
  520. res := Kernel.MessageBox(t, s, {Kernel.mbRetry, Kernel.mbCancel});
  521. IF res = Kernel.mbCancel THEN res := cancel
  522. ELSIF res = Kernel.mbRetry THEN res := retry
  523. END
  524. ELSE*)
  525. res := cancel
  526. (*END*)
  527. ELSE
  528. res := ok
  529. END
  530. UNTIL res # retry
  531. END CheckDelete;
  532. PROCEDURE (f: File) Register (name: Files.Name; type: Files.Type; ask: BOOLEAN; OUT res: INTEGER);
  533. VAR b: INTEGER; fname: FullName; fn, nn: ShortName;
  534. BEGIN
  535. ASSERT(f.state = new, 20); ASSERT(name # "", 21);
  536. Append(f.loc.path, name, type, f.loc.maxLen, fname);
  537. CheckDelete(fname, f.loc.path, ask, res);
  538. ASSERT(res # 87, 100);
  539. IF res = ok THEN
  540. IF f.name = "" THEN
  541. f.name := fname$;
  542. OpenFile(create, f.name, f.ref, res);
  543. IF res = ok THEN
  544. f.state := exclusive; CloseFile(f, res);
  545. fn := SHORT(f.name$);
  546. END
  547. ELSE
  548. f.state := exclusive; CloseFile(f, res);
  549. fn := SHORT(f.name$); nn := SHORT(fname$);
  550. MoveFile(fn, nn, res);
  551. IF res = ok THEN
  552. f.name := fname$;
  553. fn := SHORT(f.name$);
  554. ELSE
  555. ASSERT(res # 87, 101);
  556. fn := SHORT(f.name$);
  557. b := LinLibc.remove(fn);
  558. END
  559. END
  560. END;
  561. res := Error(res)
  562. END Register;
  563. PROCEDURE (f: File) Close;
  564. VAR res: INTEGER;
  565. BEGIN (* portable *)
  566. IF f.state # closed THEN
  567. (*
  568. IF f.state = exclusive THEN
  569. CloseFile(f, res)
  570. ELSE
  571. CloseFileHandle(f, res)
  572. END
  573. *)
  574. CloseFile(f, res)
  575. END
  576. END Close;
  577. (* Locator *)
  578. PROCEDURE (loc: Locator) This* (IN path: ARRAY OF CHAR): Locator;
  579. VAR new: Locator; i: INTEGER;
  580. BEGIN
  581. IF path = "" THEN
  582. NEW(new); new^ := loc^
  583. ELSIF path[0] = "/" THEN (* absolute path *)
  584. new := NewLocator(path);
  585. new.rootLen := 0
  586. ELSIF (path[0] = "\") OR (path[0] = "/") THEN
  587. IF (path[1] = "\") OR (path[1] = "/") THEN (* network path *)
  588. new := NewLocator(path);
  589. new.rootLen := 0
  590. ELSE
  591. NEW(new); new^ := dir.startup^;
  592. new.res := invalidName;
  593. RETURN new
  594. END
  595. ELSE
  596. NEW(new); Append(loc.path, path, "", loc.maxLen, new.path);
  597. i := 0; WHILE new.path[i] # 0X DO INC(i) END;
  598. IF (new.path[i-1] = "/") OR (new.path[i-1] = "\") THEN new.path[i-1] := 0X END;
  599. new.maxLen := loc.maxLen;
  600. new.caseSens := loc.caseSens;
  601. new.rootLen := loc.rootLen
  602. END;
  603. new.res := ok;
  604. RETURN new
  605. END This;
  606. (* Reader *)
  607. PROCEDURE (r: Reader) Base (): Files.File;
  608. BEGIN (* portable *)
  609. RETURN r.base
  610. END Base;
  611. PROCEDURE (r: Reader) SetPos (pos: INTEGER);
  612. VAR f: File; org, offset, i, count, res: INTEGER; buf: Buffer;
  613. BEGIN
  614. f := r.base; ASSERT(f # NIL, 20); ASSERT(f.state # closed, 25);
  615. ASSERT(pos >= 0, 22); ASSERT(pos <= f.len, 21);
  616. offset := pos MOD bufsize; org := pos - offset;
  617. i := 0; WHILE (i # nofbufs) & (f.bufs[i] # NIL) & (org # f.bufs[i].org) DO INC(i) END;
  618. IF i # nofbufs THEN
  619. buf := f.bufs[i];
  620. IF buf = NIL THEN (* create new buffer *)
  621. NEW(buf); f.bufs[i] := buf; buf.org := -1
  622. END
  623. ELSE (* choose an existing buffer *)
  624. f.swapper := (f.swapper + 1) MOD nofbufs;
  625. FlushBuffer(f, f.swapper); buf := f.bufs[f.swapper]; buf.org := -1
  626. END;
  627. IF buf.org # org THEN
  628. IF org + bufsize > f.len THEN buf.len := f.len - org ELSE buf.len := bufsize END;
  629. count := buf.len;
  630. IF count > 0 THEN
  631. IF f.ref = invalid THEN CreateFile(f, res) (* ASSERT(res = ok, 100) *) END;
  632. IF f.ref # invalid THEN
  633. IF LinLibc.fseek(f.ref, org, LinLibc.SEEK_SET) # 0 THEN
  634. res := LinLibc_errno(); HALT(101)
  635. END;
  636. IF LinLibc.fread(SYSTEM.ADR(buf.data), 1, count, f.ref) < count THEN
  637. res := LinLibc_errno(); HALT(102)
  638. END;
  639. f.t := Kernel.Time()
  640. END
  641. END;
  642. buf.org := org; buf.dirty := FALSE
  643. END;
  644. r.buf := buf; r.org := org; r.offset := offset; r.eof := FALSE
  645. (* 0<= r.org <= r.base.len *)
  646. (* 0 <= r.offset < bufsize *)
  647. (* 0 <= r.buf.len <= bufsize *)
  648. (* r.offset <= r.base.len *)
  649. (* r.offset <= r.buf.len *)
  650. END SetPos;
  651. PROCEDURE (r: Reader) Pos (): INTEGER;
  652. BEGIN (* portable *)
  653. ASSERT(r.base # NIL, 20);
  654. RETURN r.org + r.offset
  655. END Pos;
  656. PROCEDURE (r: Reader) ReadByte (OUT x: BYTE);
  657. BEGIN (* portable *)
  658. IF (r.org # r.buf.org) OR (r.offset >= bufsize) THEN r.SetPos(r.org + r.offset) END;
  659. IF r.offset < r.buf.len THEN
  660. x := r.buf.data[r.offset]; INC(r.offset)
  661. ELSE
  662. x := 0; r.eof := TRUE
  663. END
  664. END ReadByte;
  665. PROCEDURE (r: Reader) ReadBytes (VAR x: ARRAY OF BYTE; beg, len: INTEGER);
  666. VAR from, to, count, restInBuf: INTEGER;
  667. BEGIN (* portable *)
  668. ASSERT(beg >= 0, 21);
  669. IF len > 0 THEN
  670. ASSERT(beg + len <= LEN(x), 23);
  671. WHILE len # 0 DO
  672. IF (r.org # r.buf.org) OR (r.offset >= bufsize) THEN r.SetPos(r.org + r.offset) END;
  673. restInBuf := r.buf.len - r.offset;
  674. IF restInBuf = 0 THEN r.eof := TRUE; RETURN
  675. ELSIF restInBuf <= len THEN count := restInBuf
  676. ELSE count := len
  677. END;
  678. from := SYSTEM.ADR(r.buf.data[r.offset]); to := SYSTEM.ADR(x) + beg;
  679. SYSTEM.MOVE(from, to, count);
  680. INC(r.offset, count); INC(beg, count); DEC(len, count)
  681. END;
  682. r.eof := FALSE
  683. ELSE ASSERT(len = 0, 22)
  684. END
  685. END ReadBytes;
  686. (* Writer *)
  687. PROCEDURE (w: Writer) Base (): Files.File;
  688. BEGIN (* portable *)
  689. RETURN w.base
  690. END Base;
  691. PROCEDURE (w: Writer) SetPos (pos: INTEGER);
  692. VAR f: File; org, offset, i, count, res: INTEGER; buf: Buffer;
  693. BEGIN
  694. f := w.base; ASSERT(f # NIL, 20); ASSERT(f.state # closed, 25);
  695. ASSERT(pos >= 0, 22); ASSERT(pos <= f.len, 21);
  696. offset := pos MOD bufsize; org := pos - offset;
  697. i := 0; WHILE (i # nofbufs) & (f.bufs[i] # NIL) & (org # f.bufs[i].org) DO INC(i) END;
  698. IF i # nofbufs THEN
  699. buf := f.bufs[i];
  700. IF buf = NIL THEN (* create new buffer *)
  701. NEW(buf); f.bufs[i] := buf; buf.org := -1
  702. END
  703. ELSE (* choose an existing buffer *)
  704. f.swapper := (f.swapper + 1) MOD nofbufs;
  705. FlushBuffer(f, f.swapper); buf := f.bufs[f.swapper]; buf.org := -1
  706. END;
  707. IF buf.org # org THEN
  708. IF org + bufsize > f.len THEN buf.len := f.len - org ELSE buf.len := bufsize END;
  709. count := buf.len;
  710. IF count > 0 THEN
  711. IF f.ref = invalid THEN CreateFile(f, res) (* ASSERT(res = ok, 100) *) END;
  712. IF f.ref # invalid THEN
  713. IF LinLibc.fseek(f.ref, org, LinLibc.SEEK_SET) # 0 THEN
  714. res := LinLibc_errno(); HALT(101)
  715. END;
  716. IF LinLibc.fread(SYSTEM.ADR(buf.data), 1, count, f.ref) < count THEN
  717. res := LinLibc_errno(); HALT(102)
  718. END;
  719. f.t := Kernel.Time()
  720. END
  721. END;
  722. buf.org := org; buf.dirty := FALSE
  723. END;
  724. w.buf := buf; w.org := org; w.offset := offset
  725. (* 0<= w.org <= w.base.len *)
  726. (* 0 <= w.offset < bufsize *)
  727. (* 0 <= w.buf.len <= bufsize *)
  728. (* w.offset <= w.base.len *)
  729. (* w.offset <= w.buf.len *)
  730. END SetPos;
  731. PROCEDURE (w: Writer) Pos (): INTEGER;
  732. BEGIN (* portable *)
  733. ASSERT(w.base # NIL, 20);
  734. RETURN w.org + w.offset
  735. END Pos;
  736. PROCEDURE (w: Writer) WriteByte (x: BYTE);
  737. BEGIN (* portable *)
  738. ASSERT(w.base.state # closed, 25);
  739. IF (w.org # w.buf.org) OR (w.offset >= bufsize) THEN w.SetPos(w.org + w.offset) END;
  740. w.buf.data[w.offset] := x; w.buf.dirty := TRUE;
  741. IF w.offset = w.buf.len THEN INC(w.buf.len); INC(w.base.len) END;
  742. INC(w.offset)
  743. END WriteByte;
  744. PROCEDURE (w: Writer) WriteBytes (IN x: ARRAY OF BYTE; beg, len: INTEGER);
  745. VAR from, to, count, restInBuf: INTEGER;
  746. BEGIN (* portable *)
  747. ASSERT(beg >= 0, 21); ASSERT(w.base.state # closed, 25);
  748. IF len > 0 THEN
  749. ASSERT(beg + len <= LEN(x), 23);
  750. WHILE len # 0 DO
  751. IF (w.org # w.buf.org) OR (w.offset >= bufsize) THEN w.SetPos(w.org + w.offset) END;
  752. restInBuf := bufsize - w.offset;
  753. IF restInBuf <= len THEN count := restInBuf ELSE count := len END;
  754. from := SYSTEM.ADR(x) + beg; to := SYSTEM.ADR(w.buf.data[w.offset]);
  755. SYSTEM.MOVE(from, to, count);
  756. INC(w.offset, count); INC(beg, count); DEC(len, count);
  757. IF w.offset > w.buf.len THEN INC(w.base.len, w.offset - w.buf.len); w.buf.len := w.offset END;
  758. w.buf.dirty := TRUE
  759. END
  760. ELSE ASSERT(len = 0, 22)
  761. END
  762. END WriteBytes;
  763. (* Directory *)
  764. PROCEDURE (d: Directory) This (IN path: ARRAY OF CHAR): Files.Locator;
  765. BEGIN
  766. RETURN d.startup.This(path)
  767. END This;
  768. PROCEDURE (d: Directory) New (loc: Files.Locator; ask: BOOLEAN): Files.File;
  769. VAR f: File; res: INTEGER; attr: SET; isDir: BOOLEAN; buf: LinLibc.stat_t;
  770. BEGIN
  771. ASSERT(loc # NIL, 20); f := NIL; res := ok;
  772. WITH loc: Locator DO
  773. IF loc.path # "" THEN
  774. Stat(loc.path, buf, res);
  775. IF res # ok THEN
  776. IF loc.res = 76 THEN CreateDir(loc.path, res)
  777. ELSE CheckPath(loc.path, ask, res)
  778. END
  779. ELSE
  780. ModeToAttr(buf.st_mode, attr, isDir);
  781. IF ~isDir THEN res := fileExistsErr END
  782. END
  783. END;
  784. IF res = ok THEN
  785. NEW(f); f.loc := loc; f.name := "";
  786. f.state := new; f.swapper := -1; f.len := 0; f.ref := invalid
  787. END
  788. ELSE res := invalidNameErr
  789. END;
  790. loc.res := Error(res);
  791. RETURN f
  792. END New;
  793. PROCEDURE (d: Directory) Temp (): Files.File;
  794. VAR f: File;
  795. BEGIN
  796. NEW(f); f.loc := d.temp; f.name := "";
  797. f.state := temp; f.swapper := -1; f.len := 0; f.ref := invalid;
  798. RETURN f
  799. END Temp;
  800. PROCEDURE GetShadowDir (loc: Locator; OUT dir: FullName);
  801. VAR i, j: INTEGER;
  802. BEGIN
  803. dir := startupDir$; i := startupLen; j := loc.rootLen;
  804. WHILE loc.path[j] # 0X DO dir[i] := loc.path[j]; INC(i); INC(j) END;
  805. dir[i] := 0X
  806. END GetShadowDir;
  807. PROCEDURE (d: Directory) Old (loc: Files.Locator; name: Files.Name; shrd: BOOLEAN): Files.File;
  808. VAR res: INTEGER; f: File; ref: LinLibc.PtrFILE; fname: FullName; type: Files.Type; s: BYTE; buf: LinLibc.stat_t;
  809. BEGIN
  810. ASSERT(loc # NIL, 20); ASSERT(name # "", 21);
  811. res := ok; f := NIL;
  812. WITH loc: Locator DO
  813. Append(loc.path, name, "", loc.maxLen, fname);
  814. f := ThisFile(fname);
  815. IF f # NIL THEN
  816. IF ~shrd OR (f.state = exclusive) THEN loc.res := Error(sharingErr); RETURN NIL
  817. ELSE loc.res := ok; RETURN f
  818. END
  819. END;
  820. IF shrd THEN s := shared ELSE s := exclusive END;
  821. OpenFile(s, fname, ref, res);
  822. IF ((res = fileNotFoundErr) OR (res = pathNotFoundErr)) & (loc.rootLen > 0) THEN
  823. GetShadowDir(loc, fname);
  824. Append(fname, name, "", loc.maxLen, fname);
  825. f := ThisFile(fname);
  826. IF f # NIL THEN
  827. IF ~shrd OR (f.state = exclusive) THEN loc.res := Error(sharingErr); RETURN NIL
  828. ELSE loc.res := ok; RETURN f
  829. END
  830. END;
  831. OpenFile(s, fname, ref, res)
  832. END;
  833. IF res = ok THEN
  834. NEW(f); f.loc := loc;
  835. f.swapper := -1;
  836. GetType(name, type);
  837. f.InitType(type);
  838. ASSERT(ref # invalid, 107);
  839. f.ref := ref; f.name := fname$; f.state := s; f.t := Kernel.Time();
  840. Stat(f.name, buf, res);
  841. f.len := SHORT(buf.st_size); (* A. V. Shiryaev: OpenBSD *)
  842. res := LinLibc.fseek(ref, 0, LinLibc.SEEK_SET);
  843. END
  844. END;
  845. loc.res := Error(res);
  846. RETURN f
  847. END Old;
  848. PROCEDURE (d: Directory) Delete* (loc: Files.Locator; name: Files.Name);
  849. VAR res: INTEGER; fname: FullName;
  850. BEGIN
  851. ASSERT(loc # NIL, 20);
  852. WITH loc: Locator DO
  853. Append(loc.path, name, "", loc.maxLen, fname);
  854. Delete(fname, loc.path, res)
  855. ELSE res := invalidNameErr
  856. END;
  857. loc.res := Error(res)
  858. END Delete;
  859. PROCEDURE (d: Directory) Rename* (loc: Files.Locator; old, new: Files.Name; ask: BOOLEAN);
  860. VAR res, i: INTEGER; oldname, newname: FullName; f: File; on, nn, tn: ShortName; buf: LinLibc.stat_t;
  861. BEGIN
  862. ASSERT(loc # NIL, 20);
  863. WITH loc: Locator DO
  864. Append(loc.path, old, "", loc.maxLen, oldname); Append(loc.path, new, "", loc.maxLen, newname);
  865. on := SHORT(oldname$); nn := SHORT(newname$);
  866. Stat(oldname, buf, res);
  867. IF res = ok THEN
  868. f := ThisFile(oldname);
  869. IF (f # NIL) & (f.ref # invalid) THEN res := LinLibc.fclose(f.ref); f.ref := invalid END;
  870. IF Diff(oldname, newname, loc.caseSens) # 0 THEN
  871. CheckDelete(newname, loc.path, ask, res);
  872. IF res = ok THEN
  873. IF LinLibc.rename(on, nn) = 0 THEN
  874. IF f # NIL THEN (* still in use => update file table *)
  875. f.name := newname$
  876. END
  877. ELSE res := LinLibc_errno()
  878. END
  879. END
  880. ELSE (* destination is same file as source *)
  881. tn := on$; i := LEN(tn$) - 1;
  882. REPEAT
  883. tn[i] := SHORT(CHR(ORD(tn[i]) + 1));
  884. MoveFile(on, tn, res);
  885. UNTIL (res # fileExistsErr) & (res # alreadyExistsErr) & (res # 87);
  886. IF res = ok THEN
  887. MoveFile(tn, nn, res)
  888. END
  889. END
  890. ELSE res := fileNotFoundErr
  891. END
  892. ELSE res := invalidNameErr
  893. END;
  894. loc.res := Error(res)
  895. END Rename;
  896. PROCEDURE (d: Directory) SameFile* (loc0: Files.Locator; name0: Files.Name;
  897. loc1: Files.Locator; name1: Files.Name
  898. ): BOOLEAN;
  899. VAR p0, p1: FullName;
  900. BEGIN
  901. ASSERT(loc0 # NIL, 20); ASSERT(loc1 # NIL, 21);
  902. WITH loc0: Locator DO Append(loc0.path, name0, "", loc0.maxLen, p0) END;
  903. WITH loc1: Locator DO Append(loc1.path, name1, "", loc1.maxLen, p1) END;
  904. RETURN Diff(p0, p1, loc0(Locator).caseSens) = 0
  905. END SameFile;
  906. PROCEDURE (d: Directory) FileList* (loc: Files.Locator): Files.FileInfo;
  907. VAR diff, res: INTEGER; first, last, info: Files.FileInfo; s: FullName;
  908. ss, fname: ShortName; dirp: LinLibc.PtrDIR; dp: LinLibc.PtrDirent; buf: LinLibc.stat_t; tm: LinLibc.tm;
  909. isDir: BOOLEAN; attr: SET;
  910. BEGIN
  911. ASSERT(loc # NIL, 20);
  912. first := NIL; last :=NIL;
  913. WITH loc: Locator DO
  914. ss := SHORT(loc.path);
  915. dirp := LinLibc.opendir(ss);
  916. IF dirp # LinLibc.NULL THEN
  917. dp := LinLibc.readdir(dirp);
  918. WHILE dp # NIL DO
  919. IF (dp.d_name # ".") & (dp.d_name # "..") & (LEN(dp.d_name$) < LEN(info.name)) THEN
  920. fname := ss + "/" + dp.d_name;
  921. res := LinLibc.stat(fname, buf); (* Shiryaev A. V.: OpenBSD *)
  922. ModeToAttr(buf.st_mode, attr, isDir);
  923. IF ~isDir THEN
  924. info := first; last := NIL; s := dp.d_name$;
  925. WHILE (info # NIL) & (Diff(info.name, s, loc.caseSens) < 0) DO last := info; info := info.next END;
  926. NEW(info);
  927. info.name := dp.d_name$;
  928. GetType(info.name, info.type);
  929. info.length := SHORT(buf.st_size); (* A. V. Shiryaev: OpenBSD *)
  930. tm := LinLibc.localtime(buf.st_mtime);
  931. IF tm # NIL THEN
  932. info.modified.year := tm.tm_year + 1900;
  933. info.modified.month := tm.tm_mon + 1;
  934. info.modified.day := tm.tm_mday;
  935. info.modified.hour := tm.tm_hour;
  936. info.modified.minute := tm.tm_min;
  937. info.modified.second := tm.tm_sec
  938. END;
  939. info.attr := attr;
  940. IF last = NIL THEN info.next := first; first := info ELSE info.next := last.next; last.next := info END
  941. END
  942. END;
  943. dp := LinLibc.readdir(dirp)
  944. END;
  945. res := LinLibc.closedir(dirp)
  946. ELSE res := LinLibc_errno()
  947. END;
  948. (* check startup directory *)
  949. IF (loc.rootLen > 0) & ((res = ok) OR (res = fileNotFoundErr) OR (res = pathNotFoundErr)) THEN
  950. GetShadowDir(loc, s);
  951. ss := SHORT(s$);
  952. dirp := LinLibc.opendir(ss);
  953. IF dirp # LinLibc.NULL THEN
  954. dp := LinLibc.readdir(dirp);
  955. WHILE dp # NIL DO
  956. IF (dp.d_name # ".") & (dp.d_name # "..") & (LEN(dp.d_name$) < LEN(info.name)) THEN
  957. fname := ss + "/" + dp.d_name;
  958. res := LinLibc.stat(fname, buf); (* Shiryaev A. V.: OpenBSD *)
  959. ModeToAttr(buf.st_mode, attr, isDir);
  960. IF ~isDir THEN
  961. info := first; last := NIL; s := dp.d_name$;
  962. IF info # NIL THEN diff := Diff(info.name, s, loc.caseSens) END;
  963. WHILE (info # NIL) & (diff < 0) DO
  964. last := info; info := info.next;
  965. IF info # NIL THEN diff := Diff(info.name, s, loc.caseSens) END
  966. END;
  967. IF (info = NIL) OR (diff # 0) THEN
  968. NEW(info);
  969. info.name := dp.d_name$;
  970. GetType(info.name, info.type);
  971. info.length := SHORT(buf.st_size); (* A. V. Shiryaev: OpenBSD *)
  972. tm := LinLibc.localtime(buf.st_mtime);
  973. IF tm # NIL THEN
  974. info.modified.year := tm.tm_year + 1900;
  975. info.modified.month := tm.tm_mon + 1;
  976. info.modified.day := tm.tm_mday;
  977. info.modified.hour := tm.tm_hour;
  978. info.modified.minute := tm.tm_min;
  979. info.modified.second := tm.tm_sec
  980. END;
  981. info.attr := attr;
  982. IF last = NIL THEN info.next := first; first := info ELSE info.next := last.next; last.next := info END
  983. END
  984. END
  985. END;
  986. dp := LinLibc.readdir(dirp)
  987. END;
  988. res := LinLibc.closedir(dirp)
  989. ELSE res := LinLibc_errno()
  990. END
  991. END;
  992. loc.res := Error(res)
  993. ELSE loc.res := invalidName
  994. END;
  995. RETURN first
  996. END FileList;
  997. PROCEDURE (d: Directory) LocList* (loc: Files.Locator): Files.LocInfo;
  998. VAR diff, res: INTEGER; first, last, info: Files.LocInfo; s: FullName; isDir: BOOLEAN; attr: SET;
  999. ss, fname: ShortName; dirp: LinLibc.PtrDIR; dp: LinLibc.PtrDirent; buf: LinLibc.stat_t;
  1000. BEGIN
  1001. ASSERT(loc # NIL, 20);
  1002. first := NIL; last :=NIL;
  1003. WITH loc: Locator DO
  1004. ss := SHORT(loc.path);
  1005. dirp := LinLibc.opendir(ss);
  1006. IF dirp # LinLibc.NULL THEN
  1007. dp := LinLibc.readdir(dirp);
  1008. WHILE dp # NIL DO
  1009. IF (dp.d_name # ".") & (dp.d_name # "..") & (LEN(dp.d_name$) < LEN(info.name)) THEN
  1010. fname := ss + "/" + dp.d_name;
  1011. res := LinLibc.stat(fname, buf); (* Shiryaev A. V.: OpenBSD *)
  1012. ModeToAttr(buf.st_mode, attr, isDir);
  1013. IF isDir THEN
  1014. info := first; last := NIL; s := dp.d_name$;
  1015. WHILE (info # NIL) & (Diff(info.name, s, loc.caseSens) < 0) DO last := info; info := info.next END;
  1016. NEW(info);
  1017. info.name := dp.d_name$;
  1018. info.attr := attr;
  1019. IF last = NIL THEN info.next := first; first := info ELSE info.next := last.next; last.next := info END
  1020. END
  1021. END;
  1022. dp := LinLibc.readdir(dirp)
  1023. END;
  1024. res := LinLibc.closedir(dirp)
  1025. ELSE res := LinLibc_errno()
  1026. END;
  1027. (* check startup directory *)
  1028. IF (loc.rootLen > 0) & ((res = ok) OR (res = fileNotFoundErr) OR (res = pathNotFoundErr)) THEN
  1029. GetShadowDir(loc, s);
  1030. ss := SHORT(s$);
  1031. dirp := LinLibc.opendir(ss);
  1032. IF dirp # LinLibc.NULL THEN
  1033. dp := LinLibc.readdir(dirp);
  1034. WHILE dp # NIL DO
  1035. IF (dp.d_name # ".") & (dp.d_name # "..") & (LEN(dp.d_name$) < LEN(info.name)) THEN
  1036. fname := ss + "/" + dp.d_name;
  1037. res := LinLibc.stat(fname, buf); (* Shiryaev A. V.: OpenBSD *)
  1038. ModeToAttr(buf.st_mode, attr, isDir);
  1039. IF isDir THEN
  1040. info := first; last := NIL; s := dp.d_name$;
  1041. IF info # NIL THEN diff := Diff(info.name, s, loc.caseSens) END;
  1042. WHILE (info # NIL) & (diff < 0) DO
  1043. last := info; info := info.next;
  1044. IF info # NIL THEN diff := Diff(info.name, s, loc.caseSens) END
  1045. END;
  1046. IF (info = NIL) OR (diff # 0) THEN
  1047. NEW(info);
  1048. info.name := dp.d_name$;
  1049. info.attr := attr;
  1050. IF last = NIL THEN info.next := first; first := info ELSE info.next := last.next; last.next := info END
  1051. END
  1052. END
  1053. END;
  1054. dp := LinLibc.readdir(dirp)
  1055. END;
  1056. res := LinLibc.closedir(dirp)
  1057. ELSE res := LinLibc_errno()
  1058. END
  1059. END;
  1060. loc.res := Error(res)
  1061. ELSE loc.res := invalidName
  1062. END;
  1063. RETURN first
  1064. END LocList;
  1065. PROCEDURE (d: Directory) GetFileName (name: Files.Name; type: Files.Type; OUT filename: Files.Name);
  1066. BEGIN
  1067. Append("", name, type, LEN(filename), filename)
  1068. END GetFileName;
  1069. (** Miscellaneous **)
  1070. PROCEDURE (VAR id: Counter) Identified (): BOOLEAN;
  1071. VAR f: File;
  1072. BEGIN
  1073. f := id.obj(File);
  1074. IF f.state # closed THEN INC(id.count) END;
  1075. RETURN FALSE
  1076. END Identified;
  1077. PROCEDURE NofFiles* (): INTEGER;
  1078. VAR p: ANYPTR; cnt: Counter;
  1079. BEGIN
  1080. cnt.typ := SYSTEM.TYP(File);
  1081. cnt.count := 0; p := Kernel.ThisFinObj(cnt);
  1082. RETURN cnt.count
  1083. END NofFiles;
  1084. PROCEDURE GetModDate* (f: Files.File; VAR year, month, day, hour, minute, second: INTEGER);
  1085. VAR buf: LinLibc.stat_t; tm: LinLibc.tm;
  1086. BEGIN
  1087. ASSERT(f IS File, 20);
  1088. Stat(f(File).name, buf, res);
  1089. IF res = ok THEN
  1090. tm := LinLibc.localtime(buf.st_mtime);
  1091. IF tm # NIL THEN
  1092. year := tm.tm_year + 1900; month := tm.tm_mon + 1; day := tm.tm_mday;
  1093. hour := tm.tm_hour; minute := tm.tm_min; second := tm.tm_sec
  1094. ELSE
  1095. res := -1
  1096. END
  1097. END;
  1098. IF res # ok THEN year := 0; month := 0; day := 0; hour := 0; minute := 0; second := 0 END
  1099. END GetModDate;
  1100. PROCEDURE SetRootDir* (path: ARRAY OF CHAR);
  1101. VAR i: INTEGER;
  1102. BEGIN
  1103. dir.startup := NewLocator(path);
  1104. dir.startup.rootLen := 0; i := 0;
  1105. WHILE startupDir[i] # 0X DO INC(i) END;
  1106. startupLen := i
  1107. END SetRootDir;
  1108. (*
  1109. PROCEDURE GetName (VAR p: ARRAY OF CHAR; VAR i: INTEGER; OUT name, opt: FullName);
  1110. VAR ch, tch: CHAR; j: INTEGER;
  1111. BEGIN
  1112. j := 0; ch := p[i]; tch := " ";
  1113. WHILE ch = " " DO INC(i); ch := p[i] END;
  1114. IF (ch = "'") OR (ch = '"') THEN tch := ch; INC(i); ch := p[i] END;
  1115. WHILE (ch >= " ") & (ch # tch) DO
  1116. name[j] := ch;
  1117. IF (ch >= "a") & (ch <= "z") OR (ch >= "à") & (ch <= "ö") OR (ch >= "ø") & (ch <= "þ") THEN ch := CAP(ch)
  1118. ELSIF ch = "-" THEN ch := "/"
  1119. END;
  1120. opt[j] := ch; INC(j); INC(i); ch := p[i]
  1121. END;
  1122. IF ch > " " THEN INC(i); ch := p[i] END;
  1123. WHILE (ch # 0X) & (ch <= " ") DO INC(i); ch := p[i] END;
  1124. name[j] := 0X; opt[j] := 0X
  1125. END GetName;
  1126. PROCEDURE Init;
  1127. VAR res, i, slp: INTEGER; path, opt: FullName; attr: SET; p: LinLibc.PtrSTR; str: ARRAY 256 OF CHAR;
  1128. buf: LinLibc.stat_t; isDir: BOOLEAN;
  1129. BEGIN
  1130. (*
  1131. TODO:
  1132. Instead of using getcwd below to find the local path it would be better to use Kernel.bootinfo.argv[0].
  1133. But this only works if the PATH variable of the shell is not set to hold the BlackBox directory. In that
  1134. case all directories in the PATH variable has to be searched for the blackbox executable:
  1135. if (argv[0][0] == '/')
  1136. s = argv[0]
  1137. else {
  1138. str = getenv( "PATH" ); len = strlen( str );
  1139. for ( i = 0, s = 0; i < len; i++ )
  1140. if ( str[i] == ':' ) {
  1141. str[i] = '\0';
  1142. if ( checkpath( str + s, argv[0] ) ) break;
  1143. else s = i + 1;
  1144. }
  1145. }
  1146. *)
  1147. wildcard := "*"; NEW(dir);
  1148. str := Kernel.cmdLine$;
  1149. i := 0; slp := -1;
  1150. WHILE (str[i] # " ") & (str[i] # 0X) DO
  1151. startupDir[i] := str[i];
  1152. IF str[i] = "/" THEN slp := i END;
  1153. INC(i)
  1154. END;
  1155. startupDir[i] := 0X;
  1156. IF slp < 0 THEN
  1157. appName := startupDir;
  1158. p := NIL;
  1159. p := LinLibc.getcwd(p, 0);
  1160. startupDir := p$;
  1161. LinLibc.free(SYSTEM.VAL(LinLibc.PtrVoid, p));
  1162. i := 0;
  1163. WHILE startupDir[i] # 0X DO INC(i) END;
  1164. startupLen := i;
  1165. ELSE
  1166. i := slp + 1;
  1167. WHILE startupDir[i] # 0X DO appName[i - slp - 1] := startupDir[i]; INC(i) END;
  1168. startupDir[slp] := 0X;
  1169. startupLen := slp;
  1170. END;
  1171. dir.startup := NewLocator(startupDir);
  1172. dir.startup.rootLen := 0;
  1173. (*
  1174. p := NIL;
  1175. p := LinLibc.getcwd(p, 0);
  1176. startupDir := p$; LinLibc.free(SYSTEM.VAL(LinLibc.PtrVoid, p));
  1177. dir.startup := NewLocator(startupDir);
  1178. dir.startup.rootLen := 0; i := 0;
  1179. WHILE startupDir[i] # 0X DO INC(i) END;
  1180. startupLen := i;
  1181. str := Kernel.cmdLine$;
  1182. *)
  1183. (*
  1184. i := 0;
  1185. WHILE (str[i] # " ") & (str[i] # 0X) DO appName[i] := str[i]; INC(i) END;
  1186. appName[i] := 0X;
  1187. *)
  1188. i := 0; res := 1;
  1189. REPEAT
  1190. GetName(str, i, path, opt);
  1191. IF opt = "/USE" THEN
  1192. GetName(str, i, path, opt);
  1193. Stat(path, buf, res);
  1194. IF res =ok THEN
  1195. ModeToAttr(buf.st_mode, attr, isDir);
  1196. IF isDir THEN res := ok ELSE res := invalidName END
  1197. END
  1198. END
  1199. UNTIL (res = 0) OR (str[i] < " ");
  1200. IF serverVersion & (res = 0) THEN
  1201. i := 0; WHILE path[i] # 0X DO INC(i) END;
  1202. IF (path[i-1] = "/") OR (path[i-1] = "\") THEN DEC(i); path[i] := 0X END;
  1203. dir.startup := NewLocator(path);
  1204. dir.startup.rootLen := SHORT(i)
  1205. END;
  1206. dir.temp := NewLocator(LinLibc.P_tmpdir);
  1207. Files.SetDir(dir)
  1208. END Init;
  1209. *)
  1210. PROCEDURE Init;
  1211. CONST bbServerDir = "BB_PRIMARY_DIR"; bbWorkDir = "BB_SECONDARY_DIR";
  1212. VAR res: INTEGER; attr: SET; p: LinLibc.PtrSTR;
  1213. buf: LinLibc.stat_t; isDir, def1: BOOLEAN;
  1214. BEGIN
  1215. wildcard := "*"; NEW(dir);
  1216. p := LinLibc.getenv(bbServerDir); (* p = NIL -> undefined *)
  1217. def1 := p # NIL;
  1218. IF def1 THEN
  1219. Stat(p$, buf, res);
  1220. IF res = ok THEN
  1221. ModeToAttr(buf.st_mode, attr, isDir);
  1222. IF isDir THEN res := ok ELSE res := invalidName END
  1223. END;
  1224. def1 := res = ok;
  1225. IF def1 THEN
  1226. startupDir := p$; startupLen := LEN(startupDir$)
  1227. ELSE
  1228. Msg("HostFiles: Value of " + bbServerDir + " isn't directory, using cwd")
  1229. END
  1230. END;
  1231. IF ~def1 THEN
  1232. p := NIL;
  1233. p := LinLibc.getcwd(p, 0);
  1234. startupDir := p$; startupLen := LEN(startupDir$);
  1235. LinLibc.free(SYSTEM.VAL(LinLibc.PtrVoid, p))
  1236. END;
  1237. dir.startup := NewLocator(startupDir);
  1238. dir.startup.rootLen := 0;
  1239. p := LinLibc.getenv(bbWorkDir); (* p = NIL -> undefined *)
  1240. IF def1 & (p # NIL) THEN
  1241. Stat(p$, buf, res);
  1242. IF res = ok THEN
  1243. ModeToAttr(buf.st_mode, attr, isDir);
  1244. IF isDir THEN res := ok ELSE res := invalidName END
  1245. END;
  1246. IF ~serverVersion THEN
  1247. (* - *)
  1248. ELSIF res = ok THEN
  1249. dir.startup := NewLocator(p$); dir.startup.rootLen := LEN(p$)
  1250. ELSE
  1251. Msg("HostFiles: Value of " + bbWorkDir + " isn't directory, server configuration isn't enabled")
  1252. END
  1253. END;
  1254. dir.temp := NewLocator(LinLibc.P_tmpdir);
  1255. Files.SetDir(dir)
  1256. END Init;
  1257. BEGIN
  1258. Init
  1259. END HostFiles.