mkinsadd.pp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. {$MODE FPC}
  2. {
  3. This file is part of Free Pascal build tools
  4. Copyright (c) 2014-2015 by Tomas Hajny, member of the FPC core team.
  5. This program processes one or more listing files created with fpmake
  6. (e.g. using 'fpmake pkglist --target=<FPC_target> -zp units-' for
  7. unit packages or without the '-zp <prefix>' for utils), compares
  8. them to the text-mode installer configuration file install.dat and
  9. creates file install.add which provides information about packages
  10. missing in install.dat in a form allowing copy&paste of individual
  11. lines into install.dat.
  12. If the original description of a certain package as found in fpmake.pp
  13. is too long for install.dat, the maximum length is marked
  14. in the respective line in install.add using a pipe character ('|').
  15. See the file COPYING.FPC, included in this distribution,
  16. for details about the copyright.
  17. This program is distributed in the hope that it will be useful,
  18. but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  20. **********************************************************************}
  21. program mkinsadd;
  22. uses
  23. dos, objects;
  24. const
  25. MaxTarget = 5;
  26. TargetListShort: array [1..MaxTarget] of string [3] = ('dos', 'emx', 'os2', 'w32', 'src');
  27. TargetListLong: array [1..MaxTarget] of string = ('dos', 'emx', 'os2', '.i386-win32', '.source');
  28. DefDiffFN = 'install.add';
  29. PackageStr = 'package=';
  30. UnitsStr = 'units-';
  31. ZipExt = '.zip';
  32. type
  33. PPackageRec = ^TPackageRec;
  34. TPackageRec = object (TObject)
  35. Name, ShortName, Desc: PString;
  36. Target: byte;
  37. constructor Init (ALine: string);
  38. function GetKeyStr: string;
  39. function GetLine: string;
  40. function GetSrcLine: string;
  41. destructor Done; virtual;
  42. end;
  43. PPackageCollection = ^TPackageCollection;
  44. TPackageCollection = object (TSortedCollection)
  45. constructor Load (FN: string);
  46. function LoadFile (FN: string; DupSrc: PPackageCollection): boolean;
  47. function WriteFile (FN: string): boolean;
  48. function Compare (Key1, Key2: pointer): sw_integer; virtual;
  49. end;
  50. PDatFile = ^TDatFile;
  51. TDatFile = object (TObject)
  52. DatCollection, LstCollection: PPackageCollection;
  53. constructor LoadDat (FN: string);
  54. function ReadLstFile (FN: string): boolean;
  55. function WriteNew (FN: string): boolean;
  56. destructor Done; virtual;
  57. end;
  58. function LoCase (S: string): string;
  59. var
  60. I: longint;
  61. begin
  62. for I := 1 to Length (S) do
  63. if S [I] in ['A'..'Z'] then
  64. S [I] := char (Ord (S [I]) + 32);
  65. LoCase := S;
  66. end;
  67. constructor TPackageRec.Init (ALine: string);
  68. var
  69. I: longint;
  70. J: byte;
  71. N, SN, D, TS: string;
  72. ALine2: string;
  73. begin
  74. inherited Init;
  75. N := '';
  76. SN := '';
  77. D := '';
  78. TS := '';
  79. ALine2 := LoCase (ALine);
  80. if Copy (ALine2, 1, Length (PackageStr)) = PackageStr then
  81. begin
  82. Delete (ALine, 1, Length (PackageStr));
  83. I := Pos ('[', ALine);
  84. if I = 0 then
  85. begin
  86. I := Pos (',', ALine);
  87. if I = 0 then
  88. I := Succ (Length (ALine));
  89. end
  90. else
  91. begin
  92. SN := Copy (ALine, Succ (I), Pos (',', ALine) - I - 2);
  93. Delete (ALine, I, Length (SN) + 2);
  94. end;
  95. N := Copy (ALine, 1, Pred (I));
  96. if Length (N) <= 12 then
  97. SN := N
  98. else if (Copy (N, 1, Length (UnitsStr)) = UnitsStr) and
  99. (Length (N) - Length (UnitsStr) <= 11) then
  100. SN := 'u' + Copy (N, Succ (Length (UnitsStr)),
  101. Length (N) - Length (UnitsStr));
  102. D := Copy (ALine, Succ (I), Length (ALine) - I);
  103. end;
  104. Name := NewStr (N);
  105. if SN <> '' then
  106. ShortName := NewStr (SN)
  107. else
  108. ShortName := nil;
  109. Desc := NewStr (D);
  110. Target := 0;
  111. if SN <> '' then
  112. begin
  113. TS := LoCase (Copy (SN, Length (SN) - Length (ZipExt) - 2, 3));
  114. if Length (TS) <> 3 then
  115. TS := ''
  116. else
  117. for J := 1 to MaxTarget do
  118. if TS = TargetListShort [J] then
  119. begin
  120. Target := J;
  121. Break;
  122. end;
  123. end
  124. else
  125. begin
  126. I := Length (N) - Length (ZipExt);
  127. while (I > 0) and (N [I] <> '.') do
  128. Dec (I);
  129. if I = 0 then
  130. TS := LoCase (Copy (N, Length (SN) - Length (ZipExt) - 2, 3))
  131. else
  132. TS := LoCase (Copy (N, I, Length (N) - Length (ZipExt) - I + 1));
  133. for J := 1 to MaxTarget do
  134. if TS = TargetListLong [J] then
  135. begin
  136. Target := J;
  137. Break;
  138. end;
  139. end;
  140. if N = '' then
  141. begin
  142. WriteLn ('Err: Init failed (', ALine, ')!');
  143. Fail;
  144. end;
  145. end;
  146. destructor TPackageRec.Done;
  147. begin
  148. DisposeStr (Name);
  149. if ShortName <> nil then
  150. DisposeStr (ShortName);
  151. DisposeStr (Desc);
  152. inherited Done;
  153. end;
  154. function TPackageRec.GetKeyStr: string;
  155. var
  156. G: string;
  157. begin
  158. if ShortName <> nil then
  159. begin
  160. if Target = 0 then
  161. G := LoCase (Copy (ShortName^, 1, Length (ShortName^) - Length (ZipExt)))
  162. else
  163. G := LoCase (Copy (ShortName^, 1, Length (ShortName^) - Length (ZipExt) - 3));
  164. end
  165. else
  166. begin
  167. if Name = nil then
  168. begin
  169. GetKeyStr := '';
  170. WriteLn ('Err - GetKeyStr (nil)!');
  171. Exit;
  172. end;
  173. if Target = 0 then
  174. G := LoCase (Copy (Name^, 1, Length (Name^) - Length (ZipExt)))
  175. else
  176. begin
  177. if Copy (LoCase (Name^), 1, Length (UnitsStr)) = UnitsStr then
  178. G := 'u' + LoCase (Copy (Name^, Succ (Length (UnitsStr)),
  179. Length (Name^) - Length (UnitsStr) - Length (TargetListLong [Target])
  180. - Length (ZipExt)))
  181. else
  182. G := LoCase (Copy (Name^, 1,
  183. Length (Name^) - Length (TargetListLong [Target]) - Length (ZipExt)));
  184. end;
  185. end;
  186. G := G + '.';
  187. if Target <> 0 then
  188. G := G + TargetListShort [Target];
  189. GetKeyStr := G;
  190. end;
  191. function TPackageRec.GetLine: string;
  192. var
  193. G: string;
  194. begin
  195. G := PackageStr + Name^;
  196. if ShortName <> nil then
  197. G := G + '[' + ShortName^ + ']';
  198. if Length (Desc^) <= 45 then
  199. G := G + ',' + Desc^
  200. else
  201. G := G + ',' + Copy (Desc^, 1, 45) + '|' +
  202. Copy (Desc^, 46, Length (Desc^) - 45);
  203. GetLine := G;
  204. end;
  205. function TPackageRec.GetSrcLine: string;
  206. var
  207. GS: string;
  208. begin
  209. if Target = 0 then
  210. GS := ''
  211. else
  212. begin
  213. GS := PackageStr + Copy (Name^, 1,
  214. Length (Name^) - Length (TargetListLong [Target]) - Length (ZipExt)) +
  215. TargetListLong [MaxTarget] + ZipExt;
  216. if ShortName <> nil then
  217. GS := GS + '[' + Copy (ShortName^, 1, Length (ShortName^)
  218. - Length (TargetListShort [Target]) - Length (ZipExt)) +
  219. TargetListShort [MaxTarget] + ZipExt + ']';
  220. GS := GS + ',' + Desc^;
  221. end;
  222. GetSrcLine := GS;
  223. end;
  224. constructor TDatFile.LoadDat (FN: string);
  225. begin
  226. Init;
  227. New (DatCollection, Load (FN));
  228. New (LstCollection, Init (100, 50)); (* false? *)
  229. end;
  230. function TDatFile.ReadLstFile (FN: string): boolean;
  231. begin
  232. ReadLstFile := LstCollection^.LoadFile (FN, DatCollection);
  233. end;
  234. function TDatFile.WriteNew (FN: string): boolean;
  235. begin
  236. WriteNew := LstCollection^.WriteFile (FN);
  237. end;
  238. destructor TDatFile.Done;
  239. begin
  240. Dispose (DatCollection, Done);
  241. Dispose (LstCollection, Done);
  242. inherited Done;
  243. end;
  244. constructor TPackageCollection.Load (FN: string);
  245. begin
  246. Init (100, 50);
  247. if not (LoadFile (FN, nil)) then
  248. Fail;
  249. end;
  250. function TPackageCollection.LoadFile (FN: string; DupSrc: PPackageCollection): boolean;
  251. var
  252. F: text;
  253. S: ansistring;
  254. S2: string;
  255. P, Q: PPackageRec;
  256. I: SW_Integer;
  257. begin
  258. {$I-}
  259. Assign (F, FN);
  260. Reset (F);
  261. while not (Eof (F)) {and (LastErr = 0)} do
  262. begin
  263. S := '';
  264. ReadLn (F, S);
  265. if (Length (S) > 255) then
  266. begin
  267. WriteLn ('Error: Line too long!');
  268. WriteLn (S);
  269. Halt (255); (* Change error handling *)
  270. end;
  271. if Copy (LoCase (S), 1, Length (PackageStr)) = PackageStr then
  272. begin
  273. New (P, Init (S));
  274. if DupSrc = nil then
  275. S2 := ''
  276. else
  277. S2 := P^.GetSrcLine;
  278. if (DupSrc = nil) or not (DupSrc^.Search (P, I)) then
  279. Insert (P)
  280. else
  281. Dispose (P, Done);
  282. if S2 <> '' then
  283. begin
  284. New (Q, Init (S2));
  285. if (Q <> nil) and not (Search (Q, I)) and
  286. ((DupSrc = nil) or not (DupSrc^.Search (Q, I))) then
  287. Insert (Q)
  288. else
  289. Dispose (Q, Done);
  290. end;
  291. end;
  292. end;
  293. Close (F);
  294. LoadFile := IOResult = 0;
  295. {
  296. if P = nil then Fail else
  297. begin
  298. if P^.LastErr <> 0 then
  299. begin
  300. Dispose (P, Done);
  301. Fail;
  302. end else
  303. begin
  304. P^.ReadIni (@Self);
  305. Dispose (P, Done);
  306. end;
  307. end;
  308. }
  309. end;
  310. function TPackageCollection.WriteFile (FN: string): boolean;
  311. var
  312. F: text;
  313. S: string;
  314. P: PPackageRec;
  315. I: SW_Integer;
  316. J: byte;
  317. begin
  318. Assign (F, FN);
  319. Rewrite (F);
  320. for J := 0 to MaxTarget do
  321. for I := 0 to Count - 1 do
  322. begin
  323. P := At (I);
  324. if (P <> nil) and (P^.Target = J) then
  325. begin
  326. { Write (P^.Name^, '|');
  327. if P^.ShortName <> nil then
  328. Write (P^.ShortName^, '|')
  329. else
  330. Write ('x|');
  331. WriteLn (P^.Desc^, '|', P^.Target);
  332. WriteLn (P^.GetKeyStr);
  333. }
  334. S := P^.GetLine;
  335. (* Signalize too long description *)
  336. WriteLn (F, S);
  337. end;
  338. end;
  339. Close (F);
  340. WriteFile := IOResult = 0;
  341. end;
  342. function TPackageCollection.Compare (Key1, Key2: pointer): SW_Integer;
  343. var
  344. S1, S2: string;
  345. begin
  346. S1 := LoCase (PPackageRec (Key1)^.GetKeyStr);
  347. S2 := LoCase (PPackageRec (Key2)^.GetKeyStr);
  348. if S1 < S2 then
  349. Compare := -1
  350. else if S1 > S2 then
  351. Compare := 1
  352. else
  353. Compare := 0;
  354. end;
  355. function Base (const S: string): string;
  356. var
  357. D: DirStr;
  358. N: NameStr;
  359. E: ExtStr;
  360. begin
  361. FSplit (S, D, N, E);
  362. Base := N;
  363. end;
  364. procedure Error (const S: string; B: byte);
  365. begin
  366. WriteLn;
  367. WriteLn ('Error: ', S, '!!');
  368. Halt (B);
  369. end;
  370. procedure Syntax;
  371. begin
  372. WriteLn;
  373. WriteLn ('Syntax: ', Base (ParamStr (0)),
  374. ' <path_to_install.dat> <LstFile1> [<LstFile2>...]');
  375. WriteLn;
  376. WriteLn ('<LstFileN> files are expected to be in the format produced by fpmake');
  377. WriteLn ('(e.g. using ''fpmake pkglist --target=<FPC_target> -zp units-''');
  378. WriteLn ('for unit packages or without the ''-zp <prefix>'' parameter for utils).');
  379. WriteLn;
  380. WriteLn ('Program compares their content to the list of packages in the text-mode');
  381. WriteLn ('installer configuration file install.dat and creates file install.add');
  382. WriteLn ('with information about packages missing in install.dat in a form allowing');
  383. WriteLn ('copy&paste of individual lines into install.dat.');
  384. WriteLn;
  385. WriteLn ('If the original description of a certain package as found in fpmake.pp is');
  386. WriteLn ('too long for install.dat, the maximum length is marked in the respective line');
  387. WriteLn ('in install.add using a pipe character (''|'') to give hint for manual editing.');
  388. Halt;
  389. end;
  390. var
  391. I, J, K: byte;
  392. DAT: TDatFile;
  393. PrevCount: SW_Integer;
  394. SR: SearchRec;
  395. D: DirStr;
  396. N: NameStr;
  397. E: ExtStr;
  398. begin
  399. J := ParamCount;
  400. if J < 2 then
  401. begin
  402. WriteLn;
  403. WriteLn ('Error: Too few parameters!!');
  404. Syntax;
  405. end;
  406. DAT.LoadDat (ParamStr (1));
  407. if DAT.DatCollection <> nil then
  408. WriteLn (LineEnding +
  409. 'Source install.dat file (', ParamStr (1), ') loaded correctly: ',
  410. DAT.DatCollection^.Count, ' records')
  411. else
  412. Error ('Failure while loading source install.dat file (' + ParamStr (1) +
  413. ')', 1);
  414. K := 0;
  415. for I := 2 to J do
  416. begin
  417. FSplit (ParamStr (I), D, N, E);
  418. FindFirst (ParamStr (I), AnyFile - Directory, SR);
  419. if DosError <> 0 then
  420. Error ('No package listing file found for "' + ParamStr (I) + '"', I)
  421. else
  422. begin
  423. while (DosError = 0) do
  424. begin
  425. Inc (K);
  426. PrevCount := DAT.LstCollection^.Count;
  427. if DAT.ReadLstFile (D + SR.Name) then
  428. WriteLn ('Package listing #', K, ' (', D + SR.Name,
  429. ') loaded correctly: ', DAT.LstCollection^.Count - PrevCount,
  430. ' new records')
  431. else
  432. Error ('Failure while loading package listing (' + D + SR.Name + ')',
  433. J + K);
  434. FindNext (SR);
  435. end;
  436. FindClose (SR);
  437. end;
  438. end;
  439. WriteLn ('Total: ', DAT.LstCollection^.Count, ' new records');
  440. if DAT.WriteNew (DefDiffFN) then
  441. WriteLn ('Output file (' + DefDiffFN + ') created successfully.')
  442. else
  443. Error ('Failure while trying to write records to the output file (' +
  444. DefDiffFN + ')', Succ (J) + K);
  445. DAT.Done;
  446. end.