PageRenderTime 68ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/vied/Source/UUtils.pas

http://verinfo-tools.googlecode.com/
Pascal | 420 lines | 195 code | 34 blank | 191 comment | 21 complexity | f085792a4014e70f98e234f1f1f7bb7d MD5 | raw file
  1. {
  2. * UUtils.pas
  3. *
  4. * Miscellaneous support routines for Version Information Editor.
  5. *
  6. * $Rev: 60 $
  7. * $Date: 2011-03-24 17:26:58 +0100 (Thu, 24 Mar 2011) $
  8. *
  9. * ***** BEGIN LICENSE BLOCK *****
  10. *
  11. * Version: MPL 1.1
  12. *
  13. * The contents of this file are subject to the Mozilla Public License Version
  14. * 1.1 (the "License"); you may not use this file except in compliance with the
  15. * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
  16. *
  17. * Software distributed under the License is distributed on an "AS IS" basis,
  18. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  19. * the specific language governing rights and limitations under the License.
  20. *
  21. * The Original Code is Utils.pas.
  22. *
  23. * The Initial Developer of the Original Code is Peter Johnson
  24. * (http://www.delphidabbler.com/).
  25. *
  26. * Portions created by the Initial Developer are Copyright (C) 1998-2011 Peter
  27. * Johnson. All Rights Reserved.
  28. *
  29. * Contributor(s):
  30. * NONE
  31. *
  32. * ***** END LICENSE BLOCK *****
  33. }
  34. unit UUtils;
  35. interface
  36. uses
  37. // Delphi
  38. SysUtils;
  39. function NextField(TextLine: string; var StringStart: Integer;
  40. var Field: string; Separator: Char): Boolean;
  41. {Finds next text field in a delimited string.
  42. @param TextLine [in] String from which field is to be extracted.
  43. @param StringStart [in/out] In: index of start of field. Out: Set to start
  44. of following field.
  45. @param Field [in/out] Set to content of required field.
  46. @param Separator [in] Character used to separate fields.
  47. @return True if a field was found, False if no more fields.
  48. }
  49. procedure Replace(DelStr, InsStr: string; var S: string);
  50. {Replaces first occurence of a substring in a string with another string.
  51. @param DelStr [in] String to be replaced.
  52. @param InsStr [in] String to replace DelStr.
  53. @param S [in/out] In: String to be modified. Out: Modified string. S is
  54. not changed if DelStr is not a substring of S.
  55. }
  56. function TrimSpaces(const Str: string): string;
  57. {Trims leading and trailing spaces from a string.
  58. @param Str [in] String to be trimmed.
  59. @return Trimmed string.
  60. }
  61. function YearToStr(const TheDate: TDateTime; const InFull: Boolean): string;
  62. {Gets year part of a date as string.
  63. @param TheDate [in] Date from which year is required.
  64. @param InFull [in] True if four digit year require, False for 2 digit year.
  65. @return Required year in required format.
  66. }
  67. function RemoveExtension(const FileName: string): string;
  68. {Removes any extension from a file name.
  69. @param FileName [in] File name to be processed.
  70. @return File name without extension..
  71. }
  72. function EnsureExtension(const FileName, Filters: string;
  73. const FilterIndex: Integer): string;
  74. {Ensures a file name has an extension based on a file filter.
  75. @param FileName [in] Name of file requiring extension.
  76. @param Filters [in] Pipe delimited file filters, in format used by file
  77. dialog boxes.
  78. @param FilterIndex [in] Index of required filter in Filters.
  79. @return If FileName has extension it is returned unchanged, otherwise the
  80. extension from Filters specified by FiltErIndex is added to FileName.
  81. }
  82. function UserAppDataFolder: string;
  83. {Gets current user's application data folder.
  84. @return Required folder.
  85. }
  86. implementation
  87. uses
  88. // Delphi
  89. Classes, Windows, ShlObj, ActiveX;
  90. function NextField(TextLine: string; var StringStart: Integer;
  91. var Field: string; Separator: Char): Boolean;
  92. {Finds next text field in a delimited string.
  93. @param TextLine [in] String from which field is to be extracted.
  94. @param StringStart [in/out] In: index of start of field. Out: Set to start
  95. of following field.
  96. @param Field [in/out] Set to content of required field.
  97. @param Separator [in] Character used to separate fields.
  98. @return True if a field was found, False if no more fields.
  99. }
  100. var
  101. StringEnd: Integer; // end of string
  102. L: Integer; // length of string
  103. Done: Boolean; // loop termination flag
  104. begin
  105. // Find length of given line
  106. L := Length(TextLine);
  107. // Check if StringStart is beyond length of line
  108. if StringStart > L then
  109. begin
  110. // StringStart is beyond line end - return nul string & false - no field
  111. Field := '';
  112. Result := False;
  113. end
  114. else
  115. begin
  116. // StringStart is within line
  117. // set string end to string start & initialise loop control flag
  118. StringEnd := StringStart;
  119. Done := False;
  120. // loop while string end is within string and separator not found
  121. while (StringEnd <= L) and not Done do
  122. begin
  123. if TextLine[StringEnd] = Separator then
  124. // we have found separator - halt loop
  125. Done := True
  126. else
  127. // haven't yet found separator - try next string position
  128. StringEnd := StringEnd + 1;
  129. end;
  130. // check if we found separator
  131. if Done then
  132. // separator found: return line from StartString to just before separator
  133. Field := Copy(TextLine, StringStart, StringEnd - StringStart)
  134. else
  135. // no separator found - return line from StringStart to end of line
  136. Field := Copy(TextLine, StringStart, StringEnd - StringStart);
  137. // Set StringStart for next time to just after StringEnd
  138. StringStart := StringEnd + 1;
  139. // Succesful result
  140. Result := True;
  141. end;
  142. end;
  143. procedure Replace(DelStr, InsStr: string; var S: string);
  144. {Replaces first occurence of a substring in a string with another string.
  145. @param DelStr [in] String to be replaced.
  146. @param InsStr [in] String to replace DelStr.
  147. @param S [in/out] In: String to be modified. Out: Modified string. S is
  148. not changed if DelStr is not a substring of S.
  149. }
  150. var
  151. Start: Integer; // starting location of DelStr in S
  152. begin
  153. // Find where DelStr begins in S, if it does
  154. Start := Pos(DelStr, S);
  155. // Check if DelStr is in S, quit if it isn't
  156. if Start = 0 then
  157. Exit;
  158. // Delete DelStr from S
  159. Delete(S, Start, Length(DelStr));
  160. // Insert InsStr in S at same place DelStr was deleted
  161. Insert(InsStr, S, Start);
  162. end;
  163. function TrimLeftSpaces(const Str: string): string;
  164. {Trims spaces from left hand side of a string.
  165. @param Str [in] String to be trimmed.
  166. @return Trimmed string.
  167. }
  168. var
  169. Start: Integer; // cursor into string searching for first non-space char
  170. Finish: Integer; // position of end of string
  171. Done: Boolean; // loop control flag
  172. begin
  173. // Initialise Start to first char in string and Finish to length of string
  174. Start := 1;
  175. Finish := Length(Str);
  176. // Initialise loop
  177. Done := False;
  178. // Loop searching for first non-space character before Finish cursor
  179. while (Start <= Finish) and not Done do
  180. begin
  181. if Str[Start] <> ' ' then
  182. Done := True
  183. else
  184. Start := Start + 1;
  185. end;
  186. // Return string from Start to Finish cursors
  187. Result := Copy(Str, Start, Finish - Start + 1)
  188. end;
  189. function TrimRightSpaces(const Str: string): string;
  190. {Trims spaces from right hand side of a string.
  191. @param Str [in] String to be trimmed.
  192. @return Trimmed string.
  193. }
  194. var
  195. Finish: Integer; // cursor into string looking for last non-space char
  196. Done: Boolean; // loop control flag
  197. begin
  198. // Initialise end of string to last character in string
  199. Finish := Length(Str);
  200. // Initialise loop
  201. Done := False;
  202. // Loop searching for last non-space character before start of string
  203. while (Finish > 0) and not Done do
  204. begin
  205. if Str[Finish] <> ' ' then
  206. Done := True
  207. else
  208. Finish := Finish - 1;
  209. end;
  210. // Return all string up to Finish cursor
  211. Result := Copy(Str, 1, Finish)
  212. end;
  213. function TrimSpaces(const Str: string): string;
  214. {Trims leading and trailing spaces from a string.
  215. @param Str [in] String to be trimmed.
  216. @return Trimmed string.
  217. }
  218. begin
  219. Result := TrimLeftSpaces(TrimRightSpaces(Str));
  220. end;
  221. function YearToStr(const TheDate: TDateTime; const InFull: Boolean): string;
  222. {Gets year part of a date as string.
  223. @param TheDate [in] Date from which year is required.
  224. @param InFull [in] True if four digit year require, False for 2 digit year.
  225. @return Required year in required format.
  226. }
  227. begin
  228. if InFull then
  229. Result := FormatDateTime('yyyy', TheDate)
  230. else
  231. Result := FormatDateTime('yy', TheDate);
  232. end;
  233. function RemoveExtension(const FileName: string): string;
  234. {Removes any extension from a file name.
  235. @param FileName [in] File name to be processed.
  236. @return File name without extension.
  237. }
  238. var
  239. P: Byte; // position of start of extension in FileName
  240. begin
  241. // Find position of start of extension, if any
  242. P := Pos('.', FileName);
  243. if P > 0 then
  244. // There is an extension - remove it and return result
  245. Result := Copy(FileName, 1, P - 1)
  246. else
  247. // There is no extension - return whole file name
  248. Result := FileName;
  249. end;
  250. function EnsureExtension(const FileName, Filters: string;
  251. const FilterIndex: Integer): string;
  252. {Ensures a file name has an extension based on a file filter.
  253. @param FileName [in] Name of file requiring extension.
  254. @param Filters [in] Pipe delimited file filters, in format used by file
  255. dialog boxes.
  256. @param FilterIndex [in] Index of required filter in Filters.
  257. @return If FileName has extension it is returned unchanged, otherwise the
  258. extension from Filters specified by FiltErIndex is added to FileName.
  259. }
  260. // ---------------------------------------------------------------------------
  261. function SelectedFilter(Filter: string; Index: Integer): string;
  262. {Gets file extension from a filter string.
  263. @param Filter [in] Pipe delimited file filters, in format used by file
  264. dialog boxes.
  265. @param FilterIndex [in] Index of required filter in Filters.
  266. @return Required extension.
  267. }
  268. var
  269. List: TStringList; // list to hold extensions represented by filters
  270. BarPos: Integer; // position of a bar char (|) in a string
  271. begin
  272. // Create string to hold extensions
  273. List := TStringList.Create;
  274. try
  275. // Scan thru filter string pulling out extensions
  276. // Each filter is in form <description>|<extension> and multiple filters
  277. // are separated by another '|' character
  278. BarPos := AnsiPos('|', Filter);
  279. while BarPos > 0 do
  280. begin
  281. // strip away description (up to first bar)
  282. Filter := Copy(Filter, BarPos + 1, $FFFF);
  283. // find any bar following extension
  284. BarPos := AnsiPos('|', Filter);
  285. if BarPos > 0 then
  286. begin
  287. // there is a bar => more filters: extension occurs before bar
  288. // .. copy out extension and add to list
  289. List.Add(Copy(Filter, 1, BarPos - 1));
  290. // .. delete extension and move to start of next filter
  291. Filter := Copy(Filter, BarPos + 1, $FFFF);
  292. BarPos := AnsiPos('|', Filter);
  293. end
  294. else if Filter <> '' then
  295. // no bar => last filter: store extension if it exists
  296. List.Add(Filter);
  297. end;
  298. // Decrease index: filters have 1 based indices, string lists are 0 based
  299. Dec(Index);
  300. // Check index is valid (in range)
  301. if (Index >= 0) and (Index < List.Count) then
  302. begin
  303. // Get hold of selected extension and strip off any leading '*.'
  304. Result := List[Index];
  305. if AnsiPos('*.', Result) > 0 then
  306. Result := Copy(Result, 2, $FFFF);
  307. end
  308. else
  309. // Index out of range: return empty string
  310. Result := '';
  311. finally
  312. List.Free;
  313. end;
  314. end;
  315. // ---------------------------------------------------------------------------
  316. var
  317. Ext: string; // file's extension
  318. begin
  319. // Get extension from file
  320. Ext := ExtractFileExt(FileName);
  321. if Ext = '' then
  322. begin
  323. // File name has no extension: add required one from filter
  324. Ext := SelectedFilter(Filters, FilterIndex);
  325. Result := FileName + Ext;
  326. end
  327. else
  328. // File name already has etension: return unchanged
  329. Result := FileName;
  330. end;
  331. procedure FreePIDL(PIDL: PItemIDList);
  332. {Uses to shell allocator to free the memory used by a PIDL.
  333. @param PIDL [in] PIDL to be freed.
  334. }
  335. var
  336. Malloc: IMalloc; // shell's allocator
  337. begin
  338. // Try to get shell allocator
  339. if Succeeded(SHGetMalloc(Malloc)) then
  340. // Use allocator to free PIDL: Malloc is freed by Delphi
  341. Malloc.Free(PIDL);
  342. end;
  343. function PIDLToFolderPath(PIDL: PItemIDList): string;
  344. {Gets full path to a file system folder from a PIDL.
  345. @param PIDL [in] PIDL describing folder.
  346. @return Required folder name or '' if PIDL refers to a virtual folder.
  347. }
  348. begin
  349. // Set max length of return string
  350. SetLength(Result, MAX_PATH);
  351. // Get the path
  352. if SHGetPathFromIDList(PIDL, PChar(Result)) then
  353. Result := PChar(Result)
  354. else
  355. Result := '';
  356. end;
  357. function SpecialFolderPath(CSIDL: Integer): string;
  358. {Gets full path to a special file system folder.
  359. @param CSIDL [in] Identifier of required folder.
  360. @return Required folder or '' if the special folder is virtual or CSIDL not
  361. supported.
  362. }
  363. var
  364. PIDL: PItemIDList; // PIDL of the special folder
  365. begin
  366. Result := '';
  367. // Get PIDL for required folder
  368. if Succeeded(SHGetSpecialFolderLocation(0, CSIDL, PIDL)) then
  369. begin
  370. try
  371. // Get path from PIDL
  372. Result := PIDLToFolderPath(PIDL);
  373. finally
  374. // Free the PIDL using shell allocator
  375. FreePIDL(PIDL);
  376. end;
  377. end
  378. end;
  379. function UserAppDataFolder: string;
  380. {Gets current user's application data folder.
  381. @return Required folder.
  382. }
  383. begin
  384. Result := SpecialFolderPath(CSIDL_APPDATA);
  385. end;
  386. end.