From 6d5d37bdf8efdfb3cb7b518bff0e75aa08fadf8f Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Mon, 17 Mar 2008 06:10:11 +0000 Subject: [PATCH] Added: X2UtNamedFormat Added: SE_CREATE_GLOBAL_NAME constant --- X2UtNamedFormat.pas | 183 +++++++++++++++++++++++++++++++++++++++++ X2UtSingleInstance.pas | 3 +- 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 X2UtNamedFormat.pas diff --git a/X2UtNamedFormat.pas b/X2UtNamedFormat.pas new file mode 100644 index 0000000..e966809 --- /dev/null +++ b/X2UtNamedFormat.pas @@ -0,0 +1,183 @@ +{ + :: X2UtNamedFormat implements Format-style functionality using named + :: instead of indexed parameters. + :: + :: Last changed: $Date$ + :: Revision: $Rev$ + :: Author: $Author$ +} +unit X2UtNamedFormat; + +interface + { + AFormat uses the same format strings as SysUtils.Format, where each + format specifier may use a named instead of a numeric index. + + AParams contains alternating the parameter name and it's value. + + Note: NamedFormat works by mapping names to indices and passing the result + to SysUtils.Format. Unnamed specifiers will therefore be affected by + named specifiers! It is recommended to name all specifiers. + } + function NamedFormat(const AFormat: String; AParams: array of const): String; + + +implementation +uses + Classes, + SysUtils; + + +type + TProtectedMemoryStream = class(TMemoryStream); + + +const + SpecifierChar = '%'; + ValidNameChars = ['A'..'Z', 'a'..'z', '0'..'9']; + + +procedure StreamWriteChar(const AStream: TStream; const AValue: Char); +begin + AStream.WriteBuffer(AValue, SizeOf(Char)); +end; + + +procedure StreamWriteString(const AStream: TStream; const AValue: String); +begin + AStream.WriteBuffer(PChar(AValue)^, Length(AValue)); +end; + + +function FindNameEnd(const APosition: PChar; const AEnd: PChar): PChar; +var + position: PChar; + +begin + Result := nil; + position := APosition; + + while position < AEnd do + begin + if position^ = ':' then + begin + Result := position; + break; + end; + + if not (position^ in ValidNameChars) then + break; + + Inc(position); + end; +end; + + +function NamedFormat(const AFormat: String; AParams: array of const): String; +var + currentPos: PChar; + formatEnd: PChar; + formatStream: TMemoryStream; + formatString: String; + name: String; + nameEnd: PChar; + nameStart: PChar; + param: TVarRec; + paramIndex: Integer; + paramNames: TStringList; + paramValues: array of TVarRec; + specifierIndex: Integer; + +begin + if Length(AParams) mod 2 = 1 then + raise Exception.Create('AParams must contains a multiple of 2 number of items'); + + currentPos := PChar(AFormat); + SetLength(paramValues, 0); + + formatEnd := currentPos; + Inc(formatEnd, Length(AFormat)); + + paramNames := TStringList.Create(); + try + paramNames.CaseSensitive := False; + + formatStream := TMemoryStream.Create(); + try + { Most likely scenario; the names are longer than the replacement + indexes. } + TProtectedMemoryStream(formatStream).Capacity := Length(AFormat); + + while currentPos < formatEnd do + begin + { Search for % } + if currentPos^ = SpecifierChar then + begin + StreamWriteChar(formatStream, currentPos^); + Inc(currentPos); + + { Check if this is not an escape character } + if (currentPos < formatEnd) and (currentPos^ <> SpecifierChar) then + begin + nameStart := currentPos; + nameEnd := FindNameEnd(currentPos, formatEnd); + + if Assigned(nameEnd) then + begin + SetString(name, nameStart, nameEnd - nameStart); + + specifierIndex := paramNames.IndexOf(name); + if specifierIndex = -1 then + specifierIndex := paramNames.Add(name); + + StreamWriteString(formatStream, IntToStr(specifierIndex)); + currentPos := nameEnd; + end; + end; + end; + + StreamWriteChar(formatStream, currentPos^); + Inc(currentPos); + end; + + SetString(formatString, PChar(formatStream.Memory), formatStream.Size); + finally + FreeAndNil(formatStream); + end; + + SetLength(paramValues, paramNames.Count); + paramIndex := 0; + + while paramIndex < High(AParams) do + begin + param := AParams[paramIndex]; + + case param.VType of + vtChar: name := param.VChar; + vtString: name := param.VString^; + vtPChar: name := param.VPChar; + vtAnsiString: name := PChar(param.VAnsiString); + else + raise Exception.CreateFmt('Parameter name at index %d is not a string value', + [paramIndex div 2]); + end; + + Inc(paramIndex); + + specifierIndex := paramNames.IndexOf(name); + if specifierIndex = -1 then + raise Exception.CreateFmt('Parameter "%s" could not be found in the format string', + [name]); + + paramValues[specifierIndex] := AParams[paramIndex]; + Inc(paramIndex); + end; + finally + FreeAndNil(paramNames); + end; + + Result := Format(formatString, paramValues); +end; + + +end. diff --git a/X2UtSingleInstance.pas b/X2UtSingleInstance.pas index 52f2548..f363501 100644 --- a/X2UtSingleInstance.pas +++ b/X2UtSingleInstance.pas @@ -199,7 +199,8 @@ uses const - WindowClass = 'X2UtSingleInstance.Window'; + WindowClass = 'X2UtSingleInstance.Window'; + SE_CREATE_GLOBAL_NAME = 'SeCreateGlobalPrivilege'; var