Files.txt 44 KB

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