OdVCSBase.Mod 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. (* $VCS 1, edgar@edgarschwarz.de, 06.01.02 22:37:41 $
  2. $Log$
  3. $ 1, edgar@edgarschwarz.de, 06.01.02 22:37:41
  4. First baseline of DAVDeltav stuff.
  5. $ 1, edgar@edgarschwarz.de, 06.01.02 22:28:44
  6. Extensions for DelatV under AOS.
  7. $ 3, Edgar.Schwarz@z.zgs.de, 10 Sep 99, 22:4:40
  8. prefixLen -> PrefixLen (typo)
  9. $ 2, Edgar.Schwarz@z.zgs.de, 1 Feb 99, 22:6:51
  10. with makro flag stuff
  11. $ 1, Edgar.Schwarz@z.zgs.de, 31 Jan 99, 1:7:28
  12. $ first version for new format
  13. *)
  14. MODULE OdVCSBase;
  15. (* old delta format
  16. kk fol. data bytes tot. bits offset tot. bits len max. start offset max. len.
  17. ----------------------------------------------------------------
  18. 00 ss 16 5 64 KB 32 byte
  19. 01 ssl 16 13 64 KB 8 KB
  20. 10 sssl 24 13 16 MB 8 KB
  21. 11 sssslll 32 29 4 GB 512 MB
  22. *)
  23. (** new delta file format
  24. description
  25. (ci) = compressed integer in Oberon style
  26. (d) = data as array of bytes
  27. tags = numbers coded as a byte
  28. ---------
  29. DeltaFile = # newest revision of file + deltas
  30. FormatName Flags Text 1{ Diff } .
  31. FormatName = "dsfantf1" .
  32. Flags = SET{31..1,MakroBit}.
  33. Text = TextTag TextLen(ci) Text(d) .
  34. Diff = # newer before older diffs
  35. DiffTag DiffLen(ci) OldTextLen(ci)
  36. Versiontag Version(ci)
  37. DateTag DateLen(ci) Date(d)
  38. AuthorTag AuthorLen(ci) Author(d)
  39. LogTextTag LogTextLen(ci) LogText(d)
  40. { DeltaAddTag AddLen(ci) AddData(d)
  41. | DeltaCopyTag CopyLen(ci) CopyOffset(ci) }
  42. [ AttachmentTag AttachmentLen(ci) Attachment(d) ] .
  43. *)
  44. (*
  45. Michael Pitra, crea@wildsau.idv-edu.uni-linz.ac.at
  46. Dialog.Open Versions.Dlg
  47. implementatory details:
  48. I used an algorithm based on one invented by Christoph Reichenberger.
  49. [Delta Storage for Arbitrary Non-Text Files,
  50. Proceedings of the 3rd International Workshop on Software Configuration Management,
  51. Trondheim, Norway, June 12-14, 1991]
  52. The delta file consists of 3 sorts of different commandos, each commando only needs one byte:
  53. end [0,0,0,0,0,0,0,0]
  54. ends delta file
  55. add [0,n,n,n,n,n,n,n]
  56. initiates an add of the following x bytes. If there are more than 127 bytes to add,
  57. just more than one add commando take place
  58. copy [1,k,k,n,n,n,n,n]
  59. There are 4 different forms of the copy command:
  60. s ... start offset byte
  61. l ... length byte (SHL 5)
  62. n ... lowest 5 bits of length
  63. The version control system file is using following format:
  64. the delta1 ... files are of the format described above.
  65. Last Update:
  66. *)
  67. IMPORT SYSTEM, Dates, Strings, Files, Out := KernelLog, Clock;
  68. CONST
  69. BaseDir* = "FTP:/WebDAV/repo/";
  70. TmpDFile="FTP:/WebDAV/repo/VCSBaseD.Temp"; (* New delta file. TODO: Better with <name>.Temp ? *)
  71. FormatLen* = 8;
  72. FormatName* ="dsfantf1"; (* delta storage for arbitrary non-text files,
  73. invented by Christoph Reichenberger, format 0 is the original one by
  74. Michael Pitras implementation *)
  75. (* up to 32 flags *)
  76. MakroBit* = 0; (* makro expansion yes/no *)
  77. (* some tags for log info *)
  78. VersionTag = 1X;
  79. DateTag = 2X;
  80. AuthorTag = 3X;
  81. LogTextTag = 4X;
  82. DeltaAddTag = 5X;
  83. DeltaCopyTag = 6X;
  84. AttachmentTag = 7X;
  85. TextTag = 8X;
  86. DiffTag = 9X;
  87. AccessTag = 0AX;
  88. HashLen = 16381; (* prime near 2^15 *)
  89. D = 256; (* byte-oriented *)
  90. PrefixLen = 7;
  91. MaxVersions* = 100; (* !!! should be removed (es) *)
  92. TYPE
  93. PLinkNode = POINTER TO TLinkNode;
  94. TLinkNode = RECORD
  95. next: PLinkNode;
  96. pos: LONGINT;
  97. END;
  98. THashList = ARRAY HashLen OF PLinkNode;
  99. PHashList = POINTER TO THashList;
  100. TWorkBytes = ARRAY PrefixLen OF CHAR;
  101. TData = POINTER TO ARRAY OF CHAR;
  102. TLog* = RECORD
  103. versionID*: LONGINT;
  104. author*: ARRAY 127 OF CHAR;
  105. logText*: ARRAY 256 OF CHAR;
  106. date*: ARRAY 22 OF CHAR;
  107. lenOfDelta: LONGINT;
  108. lenOfOld: LONGINT; (* file length of old version to allocate
  109. a matching buffer *)
  110. flags* : SET;
  111. END;
  112. TFileName* = ARRAY 256 OF CHAR;
  113. TDeltaEntry* = ARRAY 20 OF CHAR;
  114. TDList* = ARRAY MaxVersions OF TDeltaEntry;
  115. VAR
  116. errMsg*: ARRAY 256 OF CHAR;
  117. formatStr: ARRAY FormatLen+1 OF CHAR;
  118. res: WORD;
  119. (* Split a filename in it's directory part and member name.
  120. filename = [ filesystem ":" ] ["/"] [ dir "/" ] base. *)
  121. PROCEDURE splitDirBase(fileName: ARRAY OF CHAR; VAR dir, base: ARRAY OF CHAR);
  122. CONST CollCh = "/"; FileSystemCh = ":";
  123. VAR collPos, len, i: LONGINT;
  124. BEGIN
  125. len := Strings.Length(fileName);
  126. (* Get last collection delimiter. *)
  127. collPos := -1;
  128. LOOP
  129. FOR i := 0 TO len -1 DO
  130. IF fileName[len-i] = CollCh THEN
  131. collPos := len-i;
  132. EXIT;
  133. ELSIF fileName[len-i] = FileSystemCh THEN
  134. EXIT;
  135. END;
  136. END;
  137. END;
  138. IF collPos = -1 THEN
  139. COPY("", dir); COPY(fileName, base);
  140. ELSE
  141. FOR i := 0 TO collPos-1 DO dir[i] := fileName[i]; END;
  142. dir[collPos] := 0X;
  143. FOR i := collPos+1 TO len -1 DO base[i-collPos-1] := fileName[i]; END;
  144. base[i-collPos] := 0X;
  145. END;
  146. END splitDirBase;
  147. (** Create directory for a filename if it doesn't exist. *)
  148. PROCEDURE makeDirs(name: ARRAY OF CHAR): WORD;
  149. VAR
  150. dir, base: ARRAY 128 OF CHAR;
  151. res: WORD;
  152. dirFile: Files.File;
  153. BEGIN
  154. Files.SplitPath(name, dir, base);
  155. dirFile := Files.Old(dir);
  156. IF dirFile = NIL THEN
  157. Files.CreateDirectory(dir, res);
  158. RETURN res;
  159. ELSE
  160. RETURN 0; (* Directory already exists. *)
  161. END;
  162. END makeDirs;
  163. PROCEDURE DateTime*(VAR s: ARRAY OF CHAR);
  164. VAR date, time: LONGINT; dateTime: Dates.DateTime; timeStr: ARRAY 16 OF CHAR;
  165. BEGIN
  166. Clock.Get(time, date);
  167. dateTime := Dates.OberonToDateTime(date, time);
  168. Strings.DateToStr(dateTime, s); Strings.Append(s, " ");
  169. Strings.TimeToStr(dateTime, timeStr); Strings.Append(s, timeStr);
  170. END DateTime;
  171. (** Hashing stuff *)
  172. PROCEDURE Hash(toHash: TWorkBytes): LONGINT;
  173. VAR
  174. i, h: LONGINT;
  175. BEGIN
  176. h := 0;
  177. FOR i := 0 TO PrefixLen - 1 DO
  178. h := (h * D + ORD(toHash[i])) MOD HashLen;
  179. END;
  180. RETURN h;
  181. END Hash;
  182. PROCEDURE AccessArray(arr: TData; len, left, right: LONGINT; VAR ret: ARRAY OF CHAR);
  183. VAR
  184. i: LONGINT;
  185. BEGIN
  186. IF (arr = NIL) OR (left > len - 1) OR (right > len - 1) THEN RETURN END;
  187. IF left > right THEN i := left; left := right; right := i; END;
  188. FOR i := left TO right DO
  189. ret[i-left] := arr[i];
  190. END;
  191. END AccessArray;
  192. PROCEDURE BuildLinkList(new: TData; lenNew: LONGINT; hashList: PHashList);
  193. VAR
  194. actBytes: TWorkBytes;
  195. i, h: LONGINT;
  196. oldNode, newNode: PLinkNode;
  197. BEGIN
  198. IF new = NIL THEN RETURN END;
  199. FOR i := 0 TO lenNew - PrefixLen - 1 DO
  200. AccessArray(new, lenNew, i, i + PrefixLen - 1, actBytes);
  201. h := Hash(actBytes);
  202. NEW(newNode);
  203. newNode.pos := i;
  204. newNode.next := NIL;
  205. IF hashList[h] = NIL THEN
  206. hashList[h] := newNode;
  207. ELSE
  208. oldNode := hashList[h];
  209. WHILE oldNode.next # NIL DO oldNode := oldNode.next; END;
  210. oldNode.next := newNode;
  211. END;
  212. END;
  213. END BuildLinkList;
  214. PROCEDURE FindLongest(old, new: TData; lenOld, lenNew, oldPos: LONGINT;
  215. VAR copyStart: PLinkNode; VAR copyLen: LONGINT; hashList: PHashList);
  216. VAR
  217. work: TWorkBytes;
  218. h, n: LONGINT;
  219. start: PLinkNode;
  220. BEGIN
  221. AccessArray(old, lenOld, oldPos, oldPos + PrefixLen - 1, work);
  222. h := Hash(work);
  223. start := hashList[h];
  224. copyLen := 0;
  225. WHILE start # NIL DO (* sentinel is the nil-element in hashList and linkList *)
  226. n := 0;
  227. WHILE (oldPos+n < lenOld) & (start.pos+n < lenNew) & (old[oldPos+n] = new[start.pos+n]) DO
  228. INC(n);
  229. END;
  230. (* Find maximal n such that
  231. old[oldPos..oldPos+n-1] = new[start..start+n-1] *)
  232. IF (oldPos+n <= lenOld) & (start.pos+n <= lenNew) & (n > copyLen) THEN
  233. copyLen := n; copyStart := start;
  234. END;
  235. start := start.next;
  236. END;
  237. (* Out.String("ol,nl,op,n"); Out.Int(lenOld,5); Out.Int(lenNew,5);
  238. Out.Int(oldPos,5); Out.Int(n,5); Out.Ln; *)
  239. END FindLongest;
  240. (** Delta stuff *)
  241. (** DeltaAddTag AddLen(ci) AddData(d) *)
  242. PROCEDURE EmitAdd(old: TData; VAR dr: Files.Rider; offset, length: LONGINT);
  243. VAR
  244. i: LONGINT;
  245. BEGIN
  246. dr.file.Write(dr, DeltaAddTag);
  247. Files.WriteNum(dr, length);
  248. FOR i := 0 TO length-1 DO dr.file.Write(dr, old[offset + i]); END;
  249. END EmitAdd;
  250. (** DeltaCopyTag CopyLen(ci) CopyOffset(ci) *)
  251. PROCEDURE EmitCopy(VAR dr: Files.Rider; offset, length: LONGINT);
  252. BEGIN
  253. dr.file.Write(dr, DeltaCopyTag);
  254. Files.WriteNum(dr, length);
  255. Files.WriteNum(dr, offset);
  256. END EmitCopy;
  257. (** add delta information to diff data *)
  258. PROCEDURE CreateDelta*(old, new: TData; VAR dr: Files.Rider;
  259. lenOld, lenNew: LONGINT);
  260. VAR
  261. oldPos, addStart, copyLen: LONGINT;
  262. copyStart: PLinkNode;
  263. hashList: PHashList;
  264. BEGIN
  265. NEW(hashList);
  266. BuildLinkList(new, lenNew, hashList);
  267. oldPos := 0;
  268. addStart := 0;
  269. WHILE oldPos < lenOld - PrefixLen DO
  270. FindLongest(old, new, lenOld, lenNew, oldPos, copyStart, copyLen,
  271. hashList);
  272. IF copyLen >= PrefixLen THEN (* block move found *)
  273. IF addStart < oldPos THEN (* emit pending add command *)
  274. EmitAdd(old, dr, addStart, (oldPos (* -1 *) )-addStart);
  275. (* Out.String("add: "); Out.Int(addStart, 5); Out.String(", ");
  276. Out.Int((oldPos-1)-addStart, 5); Out.Ln;*)
  277. END;
  278. EmitCopy(dr, copyStart.pos, copyLen);
  279. (*Out.String("cpy: "); Out.Int(copyStart.pos, 5); Out.String(", ");
  280. Out.Int(copyLen, 5); Out.Ln; *)
  281. oldPos := oldPos + copyLen;
  282. addStart := oldPos;
  283. ELSE (* old[oldPos] must be marked for adding *)
  284. INC(oldPos);
  285. END;
  286. END;
  287. IF addStart < lenOld -1 THEN
  288. EmitAdd(old, dr, addStart, (lenOld (* -1 *) )-addStart);
  289. (*Out.String("add: "); Out.Int(addStart, 5); Out.String(", ");
  290. Out.Int((lenOld-1)-addStart, 5); Out.Ln; *)
  291. END;
  292. hashList := NIL;
  293. END CreateDelta;
  294. (** create previous from current version *)
  295. PROCEDURE ApplyDelta*(old, new: TData; dr: Files.Rider);
  296. VAR
  297. oldPos, newPos, len, i, kk: LONGINT;
  298. tag: CHAR;
  299. BEGIN
  300. oldPos := 0;
  301. LOOP
  302. dr.file.Read(dr, tag);
  303. CASE tag OF
  304. DeltaAddTag: (* add some stuff from delta data *)
  305. Files.ReadNum(dr, len);
  306. FOR i := 0 TO len-1 DO
  307. dr.file.Read(dr, old[oldPos]); INC(oldPos);
  308. END;
  309. | DeltaCopyTag: (* copy some stuff from new version *)
  310. Files.ReadNum(dr, len);
  311. Files.ReadNum(dr, newPos);
  312. FOR i := 0 TO len - 1 DO
  313. old[oldPos] := new[newPos + i]; INC(oldPos);
  314. END;
  315. ELSE
  316. EXIT
  317. END;
  318. END;
  319. END ApplyDelta;
  320. PROCEDURE NameToDelta(name: TFileName; VAR df: TFileName);
  321. VAR
  322. i, ofs: LONGINT;
  323. BEGIN
  324. i := Strings.Pos(":", name);
  325. IF i = -1 THEN (* Add BaseDir *)
  326. df := BaseDir;
  327. ofs := Strings.Length(df);
  328. ELSE
  329. ofs := 0;
  330. END;
  331. i := 0;
  332. WHILE (name[i] # 0X) DO
  333. df[i+ofs] := name[i];
  334. INC(i);
  335. END;
  336. df[i+ofs] := "."; df[i+ofs+1] := "V"; df[i+ofs+2] := "C"; df[i+ofs+3] := "S"; df[i+ofs+4] := 0X;
  337. END NameToDelta;
  338. PROCEDURE NameToBak(name: TFileName; VAR df: TFileName);
  339. VAR
  340. i: INTEGER;
  341. BEGIN
  342. i := 0;
  343. WHILE (name[i] # 0X) DO
  344. df[i] := name[i];
  345. INC(i);
  346. END;
  347. df[i] := "."; df[i+1] := "B"; df[i+2] := "a"; df[i+3] := "k"; df[i+4] := 0X;
  348. END NameToBak;
  349. (* get text length from delta file, rider is set to beginning of text *)
  350. PROCEDURE GetTextLen(VAR fr: Files.Rider; f: Files.File): LONGINT;
  351. VAR
  352. len: LONGINT;
  353. BEGIN
  354. f.Set(fr, FormatLen+4+1); (* skip format + flags(SET) + TextTag *)
  355. Files.ReadNum(fr, len);
  356. RETURN len
  357. END GetTextLen;
  358. (* get newest version number from first diff in file with rider *)
  359. PROCEDURE GetNewestVersion(fr: Files.Rider; f: Files.File): LONGINT;
  360. VAR
  361. newestVersion, len: LONGINT;
  362. tag: CHAR;
  363. df: Files.File;
  364. dfr: Files.Rider;
  365. dfn: TFileName;
  366. BEGIN
  367. f.Set(fr, FormatLen+4+1); (* skip format + flags(SET) + TextTag *)
  368. Files.ReadNum(fr, len); (* text len *)
  369. f.Set(fr, fr.file.Pos(fr)+len+1); (* skip text + DiffTag *)
  370. Files.ReadNum(fr, len); (* diff len *)
  371. Files.ReadNum(fr, len); (* old text len *)
  372. fr.file.Read(fr, tag); (* VersionTag *)
  373. Files.ReadNum(fr, newestVersion);
  374. RETURN newestVersion
  375. END GetNewestVersion;
  376. (* get position of diff from version n to n-1 *)
  377. PROCEDURE GetDiffPos(fr: Files.Rider; f: Files.File; n: LONGINT): LONGINT;
  378. VAR
  379. diffPos, nextDiffPos, len, diffLen, version: LONGINT;
  380. tag: CHAR;
  381. df: Files.File;
  382. dfr: Files.Rider;
  383. dfn: TFileName;
  384. BEGIN
  385. f.Set(fr, FormatLen+4+1); (* skip format + flags(SET) + TextTag *)
  386. Files.ReadNum(fr, len); (* text len *)
  387. (* Out.String("GetDiffPos"); Out.Int(n,5); Out.Ln; *)
  388. diffPos := fr.file.Pos(fr)+len;
  389. LOOP
  390. f.Set(fr, diffPos+1); (* skip DiffTag *)
  391. Files.ReadNum(fr, diffLen); (* diff len *)
  392. nextDiffPos := fr.file.Pos(fr) + diffLen;
  393. Files.ReadNum(fr, len); (* old text len *)
  394. fr.file.Read(fr, tag); (* VersionTag *)
  395. Files.ReadNum(fr, version);
  396. (* Out.Int(diffPos,10); Out.Int(version, 10); Out.Ln;*)
  397. IF version <= n THEN
  398. EXIT; (* got it *)
  399. ELSE
  400. diffPos := nextDiffPos;
  401. END;
  402. END;
  403. RETURN diffPos
  404. END GetDiffPos;
  405. (** get newest version number from first diff in file by name *)
  406. PROCEDURE Init*(name: TFileName): LONGINT;
  407. VAR
  408. newestVersion: LONGINT;
  409. df: Files.File;
  410. dfr: Files.Rider;
  411. dfn: TFileName;
  412. BEGIN
  413. NameToDelta(name, dfn);
  414. df := Files.Old(dfn);
  415. IF df = NIL THEN
  416. errMsg := " *.VCS file not found ";
  417. newestVersion := -1;
  418. ELSE
  419. newestVersion := GetNewestVersion(dfr, df);
  420. END;
  421. RETURN newestVersion
  422. END Init;
  423. (** get log data for version n *)
  424. PROCEDURE GetLog*(name: TFileName; n: LONGINT; VAR log: TLog);
  425. VAR
  426. diffLen, diffStart, num: LONGINT;
  427. textLen: LONGINT;
  428. dFileName: TFileName;
  429. df: Files.File;
  430. dr: Files.Rider;
  431. tag: CHAR;
  432. BEGIN
  433. NameToDelta(name, dFileName);
  434. df := Files.Old(dFileName);
  435. IF df=NIL THEN
  436. log.author := ""; (*"no author";*)
  437. log.logText := ""; (*"no log text";*)
  438. log.versionID := -1;
  439. log.date := "";
  440. log.flags := {};
  441. RETURN;
  442. END;
  443. IF n > GetNewestVersion(dr, df) THEN
  444. log.author := ""; (*"no author";*)
  445. log.logText := ""; (*"no log text";*)
  446. log.versionID := -1;
  447. log.date := "";
  448. log.flags := {};
  449. RETURN;
  450. END;
  451. (* get flags *)
  452. df.Set(dr, FormatLen); Files.ReadSet(dr, log.flags);
  453. (* look for version loginfo *)
  454. textLen := GetTextLen(dr, df); (* dr is at be beginning of text now *)
  455. df.Set(dr, dr.file.Pos(dr)+textLen);
  456. LOOP
  457. dr.file.Read(dr, tag); (* DiffTag *)
  458. IF tag # DiffTag THEN
  459. Out.String("DiffTag expected"); Out.Ln;
  460. END;
  461. Files.ReadNum(dr, diffLen);
  462. diffStart := dr.file.Pos(dr);
  463. Files.ReadNum(dr, log.lenOfOld); (* old text len *)
  464. dr.file.Read(dr, tag); (* VersionTag *)
  465. IF tag # VersionTag THEN
  466. Out.String("VersionTag expected"); Out.Ln;
  467. END;
  468. Files.ReadNum(dr, log.versionID);
  469. (* Out.String("GetLog:"); Out.Int(log.versionID, 5);
  470. Out.Int(n, 5); Out.Ln;
  471. *)
  472. IF log.versionID > n THEN
  473. (* go to next difference *)
  474. df.Set(dr, diffStart+diffLen);
  475. ELSIF log.versionID < n THEN
  476. (* version not found *)
  477. dr.eof := TRUE; RETURN;
  478. ELSE (* found my version *)
  479. LOOP
  480. dr.file.Read(dr, tag);
  481. IF dr.eof THEN RETURN END; (* looking for last log *)
  482. CASE tag OF
  483. DiffTag: (* end of diff reached *)
  484. RETURN;
  485. | DateTag:
  486. Files.ReadNum(dr, num);
  487. dr.file.ReadBytes(dr, log.date, 0, num);
  488. | AuthorTag:
  489. Files.ReadNum(dr, num);
  490. dr.file.ReadBytes(dr, log.author, 0, num);
  491. | LogTextTag:
  492. Files.ReadNum(dr, num);
  493. dr.file.ReadBytes(dr, log.logText, 0, num);
  494. RETURN;
  495. ELSE
  496. (* discard and go on *)
  497. Files.ReadNum(dr, num);
  498. df.Set(dr, dr.file.Pos(dr)+num);
  499. END;
  500. END;
  501. RETURN;
  502. END;
  503. END;
  504. END GetLog;
  505. (** get delta information for a version, set dr to beginning *)
  506. PROCEDURE GetDelta*(name: TFileName; n: LONGINT; VAR df: Files.File;
  507. VAR dr: Files.Rider);
  508. VAR
  509. diffStart, diffLen, version: LONGINT;
  510. dFileName: TFileName;
  511. log: TLog;
  512. tag: CHAR; num: LONGINT;
  513. BEGIN
  514. IF df = NIL THEN
  515. NameToDelta(name, dFileName);
  516. df := Files.Old(dFileName);
  517. END;
  518. df.Set(dr, FormatLen+4); (* skip format + flags *)
  519. dr.file.Read(dr, tag); (* TextTag *)
  520. Files.ReadNum(dr, num);
  521. df.Set(dr, dr.file.Pos(dr)+num); (* skip text *)
  522. LOOP
  523. dr.file.Read(dr, tag); (* DiffTag *)
  524. IF tag # DiffTag THEN
  525. Out.String("DiffTag expected"); Out.Ln;
  526. END;
  527. Files.ReadNum(dr, diffLen);
  528. diffStart := dr.file.Pos(dr);
  529. Files.ReadNum(dr, num); (* old text len *)
  530. dr.file.Read(dr, tag); (* VersionTag *)
  531. IF tag # VersionTag THEN
  532. Out.String("VersionTag expected"); Out.Ln;
  533. END;
  534. Files.ReadNum(dr, version);
  535. IF version > n THEN
  536. (* go to next difference *)
  537. df.Set(dr, diffStart+diffLen);
  538. ELSIF version < n THEN
  539. (* version not found *)
  540. dr.eof := TRUE; RETURN;
  541. ELSE (* found my version *)
  542. LOOP (* look for beginning of delta information in diff data *)
  543. dr.file.Read(dr, tag);
  544. CASE tag OF
  545. DeltaAddTag, DeltaCopyTag:
  546. df.Set(dr, dr.file.Pos(dr)-1); RETURN;
  547. | DiffTag:
  548. (* no delta found *)
  549. dr.eof := TRUE; RETURN;
  550. ELSE
  551. (* other tag, get next one *)
  552. Files.ReadNum(dr, num);
  553. df.Set(dr, dr.file.Pos(dr)+num);
  554. END;
  555. END;
  556. END;
  557. END;
  558. END GetDelta;
  559. (** set log data for a version *)
  560. PROCEDURE SetLog*(name: TFileName; n: INTEGER; log: TLog);
  561. VAR
  562. nrOfDeltas: INTEGER;
  563. fPos: LONGINT;
  564. dFileName: TFileName;
  565. oldLog: TLog;
  566. df: Files.File;
  567. dr: Files.Rider;
  568. bytes: POINTER TO ARRAY OF CHAR;
  569. BEGIN
  570. NameToDelta(name, dFileName);
  571. df := Files.Old(dFileName);
  572. IF df=NIL THEN RETURN; END;
  573. df.Set(dr, 0);
  574. Files.ReadInt(dr, nrOfDeltas);
  575. IF n > nrOfDeltas THEN RETURN; END;
  576. Files.ReadLInt(dr, fPos);
  577. REPEAT
  578. NEW(bytes, SIZEOF(TLog));
  579. dr.file.ReadBytes(dr, bytes^, 0, SIZEOF(TLog));
  580. SYSTEM.MOVE(ADDRESSOF(bytes^), ADDRESSOF(oldLog), SIZEOF(TLog));
  581. IF oldLog.versionID=n THEN
  582. df.Set(dr, dr.file.Pos(dr)-SIZEOF(TLog));
  583. SYSTEM.MOVE(ADDRESSOF(log), ADDRESSOF(bytes^), SIZEOF(TLog));
  584. dr.file.WriteBytes(dr, bytes^, 0, SIZEOF(TLog));
  585. END;
  586. IF oldLog.lenOfDelta > 0 THEN
  587. df.Set(dr, dr.file.Pos(dr)+oldLog.lenOfDelta);
  588. END;
  589. UNTIL oldLog.versionID=n;
  590. Files.Register(df);
  591. END SetLog;
  592. (** not yet implemented *)
  593. PROCEDURE GetDeltaList*(name: TFileName; VAR list: TDList): INTEGER;
  594. VAR
  595. log: TLog;
  596. i: INTEGER;
  597. BEGIN
  598. i := 1;
  599. GetLog(name, i, log);
  600. WHILE log.versionID # -1 DO
  601. COPY(log.logText, list[i-1]);
  602. INC(i);
  603. GetLog(name, i, log);
  604. END;
  605. RETURN i-1;
  606. END GetDeltaList;
  607. (* write: tag, its data length, its data *)
  608. PROCEDURE WriteTag(VAR rdr: Files.Rider;
  609. tag: CHAR; len: LONGINT; VAR data: ARRAY OF CHAR);
  610. BEGIN
  611. rdr.file.Write(rdr, tag);
  612. Files.WriteNum(rdr, len);
  613. rdr.file.WriteBytes(rdr, data, 0, len);
  614. END WriteTag;
  615. (** DeltaV.Create: Create a new version of <name> in <history path><name>.VCS. Also for initial version..*)
  616. PROCEDURE Create*(historyName, name: TFileName; log: TLog; flags: SET): LONGINT;
  617. BEGIN
  618. RETURN newVersion(historyName, name, log, flags)
  619. END Create;
  620. (** Create a new version of <name> in <name>.VCS. Also for initial version.
  621. Old variant without an explicit history path.*)
  622. PROCEDURE NewVersion*(name: TFileName; log: TLog; flags: SET): LONGINT;
  623. BEGIN
  624. RETURN newVersion(name, name, log, flags)
  625. END NewVersion;
  626. PROCEDURE newVersion(historyName, name: TFileName; log: TLog; flags: SET): LONGINT;
  627. VAR
  628. i: LONGINT;
  629. ch: CHAR;
  630. tmpDFileName, dFileName, dFileNameBak: TFileName;
  631. old, new: TData;
  632. lenNew, oldDiffStart: LONGINT;
  633. odf, ndf, f, diff: Files.File; (* old/new delta file *)
  634. odfRdr, ndfRdr, fRdr, diffRdr: Files.Rider;
  635. msg: ARRAY 256 OF CHAR;
  636. BEGIN
  637. (* Set temporary, history and history backup filenames. *)
  638. tmpDFileName := TmpDFile;
  639. (* get file for new version *)
  640. (** )Out.Enter; Out.String("VCSBase.Create: name"); Out.String(name); Out.Exit;( **)
  641. f := Files.Old(name);
  642. IF f # NIL THEN
  643. f.Set(fRdr, 0);
  644. lenNew := f.Length();
  645. NEW(new, lenNew);
  646. (* read new file version in buffer new *)
  647. fRdr.file.ReadBytes(fRdr, new^, 0, lenNew);
  648. ELSE
  649. errMsg := " file not found "; RETURN -1;
  650. END;
  651. diff := Files.New("");
  652. diff.Set(diffRdr, 0);
  653. (* open old history file *)
  654. NameToDelta(historyName, dFileName);
  655. NameToBak(dFileName, dFileNameBak);
  656. (**)Out.Enter; Out.String("VCSBase.Create: "); Out.String(name); Out.Char(" "); Out.String(dFileName); Out.Exit;(**)
  657. odf := Files.Old(dFileName);
  658. (* write new delta file header *)
  659. ndf := Files.New(TmpDFile);
  660. ndf.Set(ndfRdr, 0);
  661. (* write format string *)
  662. ndfRdr.file.WriteBytes(ndfRdr, formatStr, 0, FormatLen);
  663. (* write flags, only one flag for makro expansion for now *)
  664. Files.WriteSet(ndfRdr, flags);
  665. (* write new text *)
  666. WriteTag(ndfRdr, TextTag, lenNew, new^);
  667. (* create new diff *)
  668. IF odf # NIL THEN
  669. log.versionID := GetNewestVersion(odfRdr, odf);
  670. INC(log.versionID);
  671. log.lenOfOld := GetTextLen(odfRdr, odf);
  672. oldDiffStart := odfRdr.file.Pos(odfRdr) + log.lenOfOld;
  673. ELSE (* first version *)
  674. log.versionID := 1;
  675. log.lenOfOld := 0;
  676. END;
  677. (* collect the whole diff info in a temporary file: version, ... , delta *)
  678. Files.WriteNum(diffRdr, log.lenOfOld);
  679. diffRdr.file.Write(diffRdr, VersionTag); Files.WriteNum(diffRdr, log.versionID);
  680. IF log.date = "" THEN DateTime(log.date); END;
  681. WriteTag(diffRdr, DateTag, Strings.Length(log.date)+1, log.date);
  682. WriteTag(diffRdr, AuthorTag, Strings.Length(log.author)+1, log.author);
  683. WriteTag(diffRdr, LogTextTag,Strings.Length(log.logText)+1,log.logText);
  684. IF odf # NIL THEN
  685. (* create new delta *)
  686. NEW(old, log.lenOfOld);
  687. odfRdr.file.ReadBytes(odfRdr, old^, 0, log.lenOfOld);
  688. CreateDelta(old, new, diffRdr, log.lenOfOld, lenNew);
  689. END;
  690. (* write diff *)
  691. ndfRdr.file.Write(ndfRdr, DiffTag);
  692. Files.WriteNum(ndfRdr, diff.Length());
  693. diff.Set(diffRdr, 0);
  694. FOR i := 0 TO diff.Length() - 1 DO
  695. diffRdr.file.Read(diffRdr, ch); ndfRdr.file.Write(ndfRdr, ch);
  696. END;
  697. IF odf # NIL THEN
  698. (* copy old diffs *)
  699. odf.Set(odfRdr, oldDiffStart);
  700. FOR i := 0 TO odf.Length() - 1 - oldDiffStart DO
  701. odfRdr.file.Read(odfRdr, ch); ndfRdr.file.Write(ndfRdr, ch);
  702. END;
  703. END;
  704. Files.Register(ndf);
  705. IF odf # NIL THEN
  706. (* HACK: Backup old VCS file and primary backup. Doing a Delete dFileNameBak seems not to work if
  707. immediately followed by a rename on a FAT partition. *)
  708. Files.Delete(dFileNameBak, res);
  709. Files.Rename(dFileName, dFileNameBak, res);
  710. IF res # 0 THEN
  711. msg := "VCSBase: "; Strings.Append(msg, dFileName); Strings.Append(msg, " => ");
  712. Strings.Append(msg, dFileNameBak); Strings.Append(msg, " = ");
  713. Out.Enter; Out.String(msg); Out.Int(res, 4); Out.Exit;
  714. END;
  715. END;
  716. Files.Rename(tmpDFileName, dFileName, res);
  717. IF res # 0 THEN
  718. errMsg := "VCSBase.Create: 'Error on Rename' "; Strings.Append(errMsg, TmpDFile);
  719. Strings.Append(errMsg, " to "); Strings.Append(errMsg, dFileName);
  720. log.versionID := 0;
  721. END;
  722. RETURN log.versionID;
  723. END newVersion;
  724. (** Get a version from <name>.VCS and save it as <newFileName>.
  725. DeltaV.Select: can be used because the complete paths are already given for source and sink.
  726. *)
  727. PROCEDURE View*(name: TFileName; n: LONGINT; newFileName: TFileName): WORD;
  728. VAR
  729. version: LONGINT; res: WORD;
  730. ok: BOOLEAN;
  731. dFileName: TFileName;
  732. old, new: TData;
  733. log: TLog;
  734. lenOld, lenNew: LONGINT;
  735. df, f: Files.File;
  736. dr, r: Files.Rider;
  737. BEGIN
  738. (** ) Out.String("VCSBase.View: "); Out.String(name); Out.Char(' '); Out.String(newFileName); Out.Int(n, 3); Out.Ln; ( **)
  739. NameToDelta(name, dFileName);
  740. df := Files.Old(dFileName);
  741. IF df=NIL THEN errMsg := " file not found "; RETURN -1; END;
  742. version := GetNewestVersion(dr, df);
  743. IF n > version THEN
  744. errMsg := " not so many versions "; RETURN -1;
  745. END;
  746. (* read newest version of text in buffer new *)
  747. lenNew := GetTextLen(dr,df);
  748. NEW(new, lenNew);
  749. dr.file.ReadBytes(dr, new^, 0, lenNew);
  750. WHILE version > n DO
  751. (* apply deltas until wanted version is reached *)
  752. GetLog(name, version, log);
  753. (**
  754. Out.String("Extracting Version "); Out.Int(log.versionID, 2); Out.Ln;
  755. **)
  756. GetDelta(name, version, df, dr); (* delta to create version-1 *)
  757. (* create previous version in buffer old *)
  758. lenOld := log.lenOfOld;
  759. NEW(old, lenOld);
  760. ApplyDelta(old, new, dr);
  761. (* move old to new for next iteration *)
  762. lenNew := lenOld;
  763. new := old;
  764. DEC(version);
  765. END;
  766. (* write version to versioned file *)
  767. (** ) Out.Enter; Out.String("New"); Out.Exit; ( **)
  768. res := makeDirs(newFileName);
  769. IF res = 0 THEN
  770. f := Files.New(newFileName);
  771. IF f = NIL THEN
  772. errMsg := " couldn't create new file "; Strings.Append(errMsg, newFileName);
  773. RETURN -1;
  774. END;
  775. ELSE
  776. errMsg := " couldn't create directories for "; Strings.Append(errMsg, newFileName);
  777. RETURN res;
  778. END;
  779. (** ) Out.Enter; Out.String("Set"); Out.Exit; ( **)
  780. f.Set(r, 0);
  781. (** ) Out.Enter; Out.String("Write"); Out.Exit; ( **)
  782. r.file.WriteBytes(r, new^, 0, lenNew);
  783. (** ) Out.Enter; Out.String("Register"); Out.Exit; ( **)
  784. Files.Register(f);
  785. (** ) Out.Enter; Out.String("RETURN"); Out.Exit; ( **)
  786. RETURN version;
  787. END View;
  788. (** remove versions newer than <n> and create new working file if it doesn't exist *)
  789. PROCEDURE Extract*(name: TFileName; n: LONGINT): LONGINT;
  790. VAR
  791. version: LONGINT;
  792. ok: BOOLEAN;
  793. tmpDFileName, dFileName, dFileNameBak: TFileName;
  794. old, new: TData;
  795. log: TLog;
  796. lenOld, lenNew, lenDelta, fPos: LONGINT;
  797. tdf, df, f: Files.File;
  798. tdr, dr, r, deltaRdr: Files.Rider;
  799. i: LONGINT; ch: CHAR;
  800. flags: SET;
  801. BEGIN
  802. tmpDFileName := TmpDFile;
  803. f := Files.Old(name);
  804. IF f #NIL THEN
  805. errMsg := " don't want to overwrite working file ";
  806. RETURN -1;
  807. END;
  808. df := Files.Old(dFileName);
  809. IF df=NIL THEN
  810. errMsg := " delta file not found ";
  811. RETURN -1;
  812. END;
  813. version := GetNewestVersion(dr, df);
  814. IF n > version THEN
  815. errMsg := " not so many versions "; RETURN -1;
  816. ELSIF n = version THEN
  817. errMsg := " is newest version "; RETURN -1;
  818. END;
  819. (* read newest version of text in buffer new *)
  820. lenNew := GetTextLen(dr,df);
  821. NEW(new, lenNew);
  822. dr.file.ReadBytes(dr, new^, 0, lenNew);
  823. WHILE version>n DO
  824. GetLog(name, version, log);
  825. (**
  826. Out.String("Extracting Version "); Out.Int(log.versionID, 2); Out.Ln;
  827. **)
  828. GetDelta(name, version, df, deltaRdr); (* delta to create version-1 *)
  829. lenOld := log.lenOfOld;
  830. NEW(old, lenOld);
  831. ApplyDelta(old, new, deltaRdr);
  832. (* move old to new for next iteration *)
  833. lenNew := lenOld;
  834. new := old;
  835. DEC(version);
  836. END;
  837. (* write newest surviving text to working file *)
  838. f := Files.New(name);
  839. f.Set(r, 0);
  840. r.file.WriteBytes(r, new^, 0, lenNew);
  841. Files.Register(f);
  842. (* new delta file *)
  843. tdf := Files.New(TmpDFile);
  844. tdf.Set(tdr, 0);
  845. (* write format string *)
  846. tdr.file.WriteBytes(tdr, formatStr, 0, FormatLen);
  847. (* write flags, empty for now *)
  848. df.Set(dr, FormatLen);
  849. Files.ReadSet(dr, flags); Files.WriteSet(tdr, flags);
  850. (* write newest surviving text to delta file*)
  851. WriteTag(tdr, TextTag, lenNew, new^);
  852. (* get valid logs and deltas to new delta file *)
  853. df.Set(dr, GetDiffPos(dr, df, n));
  854. LOOP
  855. dr.file.Read(dr, ch);
  856. IF dr.eof THEN EXIT; END;
  857. tdr.file.Write(tdr, ch);
  858. END;
  859. Files.Register(tdf);
  860. (* cleanup *)
  861. (* backup old VCS file and primary backup file. *)
  862. Files.Delete(dFileNameBak, res);
  863. Files.Rename(dFileName, dFileNameBak, res);
  864. (* new delta file *)
  865. Files.Rename(tmpDFileName, dFileName, res);
  866. new := NIL; old := NIL;
  867. RETURN version;
  868. END Extract;
  869. BEGIN
  870. formatStr := FormatName;
  871. END OdVCSBase.