Unit KambiClassUtils

DescriptionusesClasses, Interfaces, Objects and RecordsFunctions and ProceduresTypesConstantsVariables

Description

Kambi class utilities.

This unit contains stuff for dealing with non-visual classes. Basically, it can be considered as the extension of Classes unit from RTL. It contains many wrappers for classes that are defined in the Classes unit and also defines some basic useful classes made entirely on my own. It also contains many global functions that deal with some classes defined in Classes unit.

Some notes about TStream descendants :

uses

Overview

Classes, Interfaces, Objects and Records

Name Description
Class TTextReader TTextReader reads given Stream line by line.
Class TKamIniFile TIniFile with minor enhancements.
Class TObjectsList_Abstract  
Class TMemoryFileStream This is TMemoryStream that at the end of construction loads it's contents from file AFileName, and (if not ReadOnly) at the beginning of destruction saves it's contents to file.
Class EStreamNotImplemented  
Class EStreamNotImplementedWrite  
Class EStreamNotImplementedSeek  
Class EStreamNotImplementedSetSize  
Class TPeekCharStream This is a purely sequential read-only stream.
Class TSimplePeekCharStream This is a simplest non-abstract implementation of abstract TPeekCharStream class.
Class TBufferedReadStream This implements abstract TPeekCharStream class, so this is purely sequential read-only stream that reads from underlying SourceStream and you can use PeekChar and ReadChar and ReadUpto routines.

Functions and Procedures

procedure FreeWithContentsAndNil(var Obj);
procedure StringsAdd(Strs: TStrings; Count: integer; itemVal: string='dummy'); overload;
procedure AddStrArrayToStrings(const StrArr: array of string; strlist: TStrings);
procedure Load_Strings(IniFile: TIniFile; const section: string; Strs: TStrings); overload;
procedure Save_Strings(IniFile: TIniFile; const section: string; Strs: TStrings); overload;
procedure AddPathsFromFileLines(slist: TStrings; const fname: string);
procedure AddPathsFromEnvironmentVar(slist: TStrings; const varname: string);
procedure AddPathsFromPathList(slist: TStrings; const pathlist: string);
function StringsSeparated(slist: TStrings; const separator: string): string;
function SearchSortedList(List: TStrings; const Value: string): Integer;
procedure Strings_AddSplittedString(Strings: TStrings; const S, Splitter: string);
procedure Strings_AddVrmlEngineProgramHelpSuffix( Strings: TStrings; const DisplayProgramName: string; const Version: string; WrapLines: boolean);
procedure Strings_SetText(SList: TStrings; const S: string);
procedure Strings_Trim(Strings: TStrings; MaxCount: Cardinal);
procedure StringList_FreeWithContentsAndNil(var StringList: TStringList);
procedure AppendStream(DestStream, OwnedSourceStream: TStream);
procedure StreamWriteLongWord(Stream: TStream; const Value: LongWord);
function StreamReadLongWord(Stream: TStream): LongWord;
procedure StreamWriteByte(Stream: TStream; const Value: Byte);
function StreamReadByte(Stream: TStream): Byte;
procedure WriteStr(Stream: TStream; const S: string); overload;
procedure WritelnStr(Stream: TStream; const S: string); overload;
procedure WriteStr(const S: string); overload;
procedure WritelnStr(const S: string); overload;
function StreamReadChar(Stream: TStream): char;
function StreamReadZeroEndString(Stream: TStream): string;
function StreamReadUpto_NotEOS(Stream: TStream; const endingChars: TSetOfChars; backEndingChar: boolean; out endingChar: char): string; overload;
function StreamReadUpto_NotEOS(Stream: TStream; const endingChars: TSetOfChars; backEndingChar: boolean): string; overload;
function StreamReadUpto_NotEOS(Stream: TStream; const endingChars: TSetOfChars; out endingChar: char): string; overload;
function StreamReadUpto_NotEOS(Stream: TStream; const endingChars: TSetOfChars ): string; overload;
function StreamReadUpto_EOS(Stream: TStream; const endingChars: TSetOfChars; backEndingChar: boolean; out endingChar: integer): string; overload;
function StreamReadUpto_EOS(Stream: TStream; const endingChars: TSetOfChars; backEndingChar: boolean): string; overload;
function StreamReadUpto_EOS(Stream: TStream; const endingChars: TSetOfChars; out endingChar: integer): string; overload;
function StreamReadUpto_EOS(Stream: TStream; const endingChars: TSetOfChars ): string; overload;
function StreamPeekChar_NotEOS(Stream: TStream): char;
function StreamPeekChar_EOS(Stream: TStream): integer;
function CreateReadFileStream(const filename: string): TStream;
procedure ReadGrowingStream(GrowingStream, DestStream: TStream; ResetDestStreamPosition: boolean);
function ReadGrowingStreamToString(GrowingStream: TStream): string;
procedure StreamWriteString(Stream: TStream; const s: string);
function StreamReadString(Stream: TStream): string;
function StreamToString(Stream: TStream): string;
procedure StreamSaveToFile(Stream: TStream; const FileName: string);
procedure CreateIfNeeded(var Component: TComponent; ComponentClass: TComponentClass; Owner: TComponent);

Types

TStringListCaseSens = TStringList;

Constants

DefaultReadBufferSize = 1024 * 1024;

Variables

StdInStream:TStream;
StdOutStream:TStream;
StdErrStream:TStream;
StdInReader: TTextReader;

Description

Functions and Procedures

procedure FreeWithContentsAndNil(var Obj);

Equivalent to FreeAndNil, but calls FreeWithContents instead of Free. Be careful — pass here only TObjectsList_Abstract descendants !

procedure StringsAdd(Strs: TStrings; Count: integer; itemVal: string='dummy'); overload;

Add some strings to TStrings

procedure AddStrArrayToStrings(const StrArr: array of string; strlist: TStrings);

Add all strings from string array to TStrings object.

procedure Load_Strings(IniFile: TIniFile; const section: string; Strs: TStrings); overload;

wersje z IniFile : zapisuja klucze o nazwach Count i ItemXxx gdzie Xxx to numer stringa w podanej sekcji. Defaultowo zostanie przyjete count = 0.

procedure Save_Strings(IniFile: TIniFile; const section: string; Strs: TStrings); overload;
 
procedure AddPathsFromFileLines(slist: TStrings; const fname: string);

Append to SList some directiories.

  • AddPathsFromFileLines reads directiories list from given file. If the file doesn't exist or is not readable, will append nothing, without raising any error. Empty lines (only whitespace) in file are ignored.

  • AddPathsFromPathList reads directiories from a list separated by PathSeparator.

  • AddPathsFromEnvironmentVar reads directiories from a list in environment variable, also separated by PathSeparator. This is suitable for parsing environment variable like $PATH or $LD_LIBRARY_PATH.

All appended directories are guaranteed to end with PathDelim.

procedure AddPathsFromEnvironmentVar(slist: TStrings; const varname: string);
 
procedure AddPathsFromPathList(slist: TStrings; const pathlist: string);
 
function StringsSeparated(slist: TStrings; const separator: string): string;

zwraca wszystkie stringi z slist sklejone separatorem.

function SearchSortedList(List: TStrings; const Value: string): Integer;

Assuming that List is sorted, searches in log time for a Value in List. Returns -1 if not found. Very useful for simple dictionaries of words, where lookup must be fast.

procedure Strings_AddSplittedString(Strings: TStrings; const S, Splitter: string);

Splits S by Splitter, and adds each splitted part to Strings. Splitting is done by Splitter, i.e. if N is the number of occurences of Splitter inside S, then it always adds N + 1 strings to Strings. Yes, this means that if S starts with Splitter then the first part is equal to ''. And if S ends with Splitter then the last oart is equal to ''.

procedure Strings_AddVrmlEngineProgramHelpSuffix( Strings: TStrings; const DisplayProgramName: string; const Version: string; WrapLines: boolean);

Something like SVrmlEngineProgramHelpSuffix, but appends contents as a couple of lines to Strings.

procedure Strings_SetText(SList: TStrings; const S: string);

Use this instead of SList.Text := S to workaround FPC 2.0.2 bug. See [http://www.freepascal.org/mantis/view.php?id=6699]

procedure Strings_Trim(Strings: TStrings; MaxCount: Cardinal);

If Strings.Count is larger than MaxCount then it will delete the last strings (to make Strings.Count = MaxCount).

procedure StringList_FreeWithContentsAndNil(var StringList: TStringList);

Free all objects within the StringList, then free and set to Nil StringList itself.

procedure AppendStream(DestStream, OwnedSourceStream: TStream);

AppendStream : skopiuj cala zawartosc strumienia OwnedSourceStream do DestStream. Potem zrob OwnedSourceStream.Free (czyli OwnedSourceStream najlepiej przekazac w postaci freshly created stream, like this AppendStream(DestStream, TMyStream.Create(...))).

procedure StreamWriteLongWord(Stream: TStream; const Value: LongWord);
 
function StreamReadLongWord(Stream: TStream): LongWord;
 
procedure StreamWriteByte(Stream: TStream; const Value: Byte);
 
function StreamReadByte(Stream: TStream): Byte;
 
procedure WriteStr(Stream: TStream; const S: string); overload;

This simply writes Length(s) bytes starting from s[1]. Versions with "ln" append nl, end of the line marker, to this. Versions without Stream param write to StdOutStream.

procedure WritelnStr(Stream: TStream; const S: string); overload;
 
procedure WriteStr(const S: string); overload;
 
procedure WritelnStr(const S: string); overload;
 
function StreamReadChar(Stream: TStream): char;

reads one char from stream using ReadBuffer (so EReadError will be raised if end of stream)

function StreamReadZeroEndString(Stream: TStream): string;
 
function StreamReadUpto_NotEOS(Stream: TStream; const endingChars: TSetOfChars; backEndingChar: boolean; out endingChar: char): string; overload;

StringReadUpto_NotEOS czyta strumien az do jakiegos znaku sposrod endingChars. Jezeli wczesniej napotka koniec strumienia - exception Stream Read Error. Zwrocony result nie zawiera endingChar. Wersja 2-argumentowa po prostu o nim "zapomina" - jezeli jej uzyjesz i backEndingChar = false, nie dowiesz sie jaki znak sposrod endingChars zatrzymal czytanie. Jezeli nie podasz backEndingChar to bedzie znaczylo ze nie ma go zwracac (tak samo jakbys podal false).

NOTE: not every stream can back characters - it is implemented as Seek(-1, soFromCurrent) and some streams in FCL/VCL simply raise exception when we're doing this ! This is a problem with TStream class, not with our code.

Use TPeekCharStream if you want to avoid this uncertainty, i.e. if you need functionality of StreamReadUpto_Xxx without using tricks with changing Position of the stream.

function StreamReadUpto_NotEOS(Stream: TStream; const endingChars: TSetOfChars; backEndingChar: boolean): string; overload;
 
function StreamReadUpto_NotEOS(Stream: TStream; const endingChars: TSetOfChars; out endingChar: char): string; overload;
 
function StreamReadUpto_NotEOS(Stream: TStream; const endingChars: TSetOfChars ): string; overload;
 
function StreamReadUpto_EOS(Stream: TStream; const endingChars: TSetOfChars; backEndingChar: boolean; out endingChar: integer): string; overload;

StringReadUpto_EOS czyta az napotka znak sposrod endingChars lub koniec strumienia. Zwraca endingChar = -1 w tym przypadku, jezeli endingChar <> -1 to mozesz swobodnie wziac Chr(endingChar) i masz znak ktory zakonczyl czytanie. Podobnie jako _NotEOS, zwrocony result nie zawiera endingChar.

function StreamReadUpto_EOS(Stream: TStream; const endingChars: TSetOfChars; backEndingChar: boolean): string; overload;
 
function StreamReadUpto_EOS(Stream: TStream; const endingChars: TSetOfChars; out endingChar: integer): string; overload;
 
function StreamReadUpto_EOS(Stream: TStream; const endingChars: TSetOfChars ): string; overload;
 
function StreamPeekChar_NotEOS(Stream: TStream): char;

_NotEOS wyrzuca blad EStreamReadError jezeli bedziemy stac na koncu stringa. Natomiast _EOS zwroci -1. Robia Peek czytajac znak a potem wracajac Seek(-1, soFromCurrent) a wiec aby dzialac wymagaja strumienia TStream na ktorym takie Seek jest dozwolone.

function StreamPeekChar_EOS(Stream: TStream): integer;
 
function CreateReadFileStream(const filename: string): TStream;

odczytaj plik ze strumienia CreateReadFileStream. CreateReadFileStream gwarantuje ze po otrzymanyn strumieniu mozna swobodnie skakac Seek'iem (czy ustawiajac Position:=) i gwarantuje ze odczyt ze strumienia bedzie buforowany (wiec mozna bez strachu o szybkosc odczytywac strumien malymi porcyjkami).

Tradycyjny strumien Delphi/FPC z modulu Classes TFileStream.Create(filename, fmOpenRead); NIE robi buforowania - jego operacje przenosza sie 1:1 na wywolania systemu operacyjnego. Pod Linuxem nawet prymitywne I/O (open/read/write) dziala szybciutko (wiem ze linux przechowuje ostatnio odczytywane dane w pamieci; przypuszcam ze plik jest po prostu zawsze odczytywany duzymi porcjami do pamieci i w zwiazku z tym nawet prymitywne IO ma jakby buforowanie), pod Windowsem proste operacje CreateFile/ReadFile/WriteFile (na ktore jest tlumaczony TFileStream) nie sa juz tak dobrze zrobione i uzywanie ponizszej proc. pod Windowsem z miejsca przyspiesza WIELE miejsc w programie i pozwala mi piszac kod czytajacy ze strumienia nie martwic sie tak bardzo o to zeby odczytywac ze strumienia duzymi porcjami. Ale oczywiscie najlepiej pod Linuxem tez uzywac ponizszej funkcji, ona bedzie zawsze "dostrojona" do aktualnego systemu operacyjnego i implementacji TFileStream.

procedure ReadGrowingStream(GrowingStream, DestStream: TStream; ResetDestStreamPosition: boolean);

this procedure reads GrowingStream (i.e. a stream that we can only read sequentially, no seeks allowed, and Size of the Stream cannot be queried.) and writes (appends) everything to DestStream.

It means that the only operation we do on GrowingStream is GrowingStream.Read and the only operation on DestStream is DestStream.WriteBuffer. So DestStream usually must be able to grow dynamically to accomodate any GrowingStream input size.

This procedure ends when GrowingStream ends. If ResetDestStreamPosition then at the end we do DestStream.Position := 0 (since it is usually useful and it would be easy for you to forget about it).

function ReadGrowingStreamToString(GrowingStream: TStream): string;

This is like ReadGrowingStream, but it returns read contents as a string.

procedure StreamWriteString(Stream: TStream; const s: string);

read and write string as Length(s) (4 bytes) + s contents (Length(s) bytes).

function StreamReadString(Stream: TStream): string;
 
function StreamToString(Stream: TStream): string;

Convert whole Stream to string. This changes Stream.Position to 0 and then reads Stream.Size bytes, so be sure that Stream supports these operations.

procedure StreamSaveToFile(Stream: TStream; const FileName: string);
 
procedure CreateIfNeeded(var Component: TComponent; ComponentClass: TComponentClass; Owner: TComponent);

If Component = nil then it will do Component := ComponentClass.Create(Owner);

Types

TStringListCaseSens = TStringList;

This is supposed to be TStringList that is case sensitive.

TODO: In FPC >= 2.0.0 TStringList.CaseSensitive property was added. However, TStringList.CaseSensitive should always be left false (because of a bug, see [http://www.freepascal.org/bugs/showrec.php3?ID=4698]). So with FPC 2.0.0 and 2.0.2 we can't have CaseSensitive TStringList.

I use this type to mark the places in my code that are vulnerable to this FPC bug.

Constants

DefaultReadBufferSize = 1024 * 1024;
 

Variables

StdInStream:TStream;

Streams that wrap standard input/output/error of the program.

Note that you can't simultaneously read from StdInStream and StdInReader (see comments at TTextReader class).

Notes for Windows:

  1. Under Windows when program is a GUI program then some (or all) of the variables below may be nil.

  2. But they don't have to be nil. User can run a GUI program and explicitly redirect it's standard stream, e.g. cat something | my_program for stdin or my_program > output.txt for stdout. Actually some shells, like Cygwin's bash, always redirect some streams "under the mask". And then you have some of std* streams available.

    Actually FPC (and Delphi ?) RTL don't provide in such cases valid Input/Output/ErrOutput variables (because IsConsole = false). But my streams below try to obtain standard stream handles under Windows regardless of IsConsole value. So even a GUI program is able to write to stdin/stdout/stderr using these streams.

  3. Unfortunately, in a GUI program under Windows you cannot depend on the fact that "StdOutStream <> nil means that stdout is actually available (because user redirected stdout etc.)". Reason ? Windows failure, as usual:

    This is tested on Windows 2000 Prof, with FPC 2.0.0 and 2.1.1 (revision 4317). When no stdout is available, StdOutStream should be nil, because GetStdHandle(STD_OUTPUT_HANDLE) should return 0. However, GetStdHandle(STD_OUTPUT_HANDLE) doesn't return 0... It returns some stupid handle (no, not INVALID_HANDLE_VALUE either) that you can't write into (trying to write returns in ERROR_INVALID_HANDLE WinAPI error). It seems that there is no way for me to check whether GetStdHandle(STD_OUTPUT_HANDLE) returned valid handle (e.g. because the program's stdout was redirected, so stdout is perfectly available) or whether it returned something unusable.

    So if you write an $apptype GUI program and you want to try to use stdout anyway, you can't just check for StdOutStream <> nil. You should also check the first write to StdOutStream for EWriteError.

    Note that GetStdHandle(STD_INPUT_HANDLE) and GetStdHandle(STD_ERROR_HANDLE) work correctly, so it should be OK to check StdInStream <> nil or StdErrStream <> nil. The only problematic one is GetStdHandle(STD_OUTPUT_HANDLE).

StdOutStream:TStream;

Streams that wrap standard input/output/error of the program.

Note that you can't simultaneously read from StdInStream and StdInReader (see comments at TTextReader class).

Notes for Windows:

  1. Under Windows when program is a GUI program then some (or all) of the variables below may be nil.

  2. But they don't have to be nil. User can run a GUI program and explicitly redirect it's standard stream, e.g. cat something | my_program for stdin or my_program > output.txt for stdout. Actually some shells, like Cygwin's bash, always redirect some streams "under the mask". And then you have some of std* streams available.

    Actually FPC (and Delphi ?) RTL don't provide in such cases valid Input/Output/ErrOutput variables (because IsConsole = false). But my streams below try to obtain standard stream handles under Windows regardless of IsConsole value. So even a GUI program is able to write to stdin/stdout/stderr using these streams.

  3. Unfortunately, in a GUI program under Windows you cannot depend on the fact that "StdOutStream <> nil means that stdout is actually available (because user redirected stdout etc.)". Reason ? Windows failure, as usual:

    This is tested on Windows 2000 Prof, with FPC 2.0.0 and 2.1.1 (revision 4317). When no stdout is available, StdOutStream should be nil, because GetStdHandle(STD_OUTPUT_HANDLE) should return 0. However, GetStdHandle(STD_OUTPUT_HANDLE) doesn't return 0... It returns some stupid handle (no, not INVALID_HANDLE_VALUE either) that you can't write into (trying to write returns in ERROR_INVALID_HANDLE WinAPI error). It seems that there is no way for me to check whether GetStdHandle(STD_OUTPUT_HANDLE) returned valid handle (e.g. because the program's stdout was redirected, so stdout is perfectly available) or whether it returned something unusable.

    So if you write an $apptype GUI program and you want to try to use stdout anyway, you can't just check for StdOutStream <> nil. You should also check the first write to StdOutStream for EWriteError.

    Note that GetStdHandle(STD_INPUT_HANDLE) and GetStdHandle(STD_ERROR_HANDLE) work correctly, so it should be OK to check StdInStream <> nil or StdErrStream <> nil. The only problematic one is GetStdHandle(STD_OUTPUT_HANDLE).

StdErrStream:TStream;

Streams that wrap standard input/output/error of the program.

Note that you can't simultaneously read from StdInStream and StdInReader (see comments at TTextReader class).

Notes for Windows:

  1. Under Windows when program is a GUI program then some (or all) of the variables below may be nil.

  2. But they don't have to be nil. User can run a GUI program and explicitly redirect it's standard stream, e.g. cat something | my_program for stdin or my_program > output.txt for stdout. Actually some shells, like Cygwin's bash, always redirect some streams "under the mask". And then you have some of std* streams available.

    Actually FPC (and Delphi ?) RTL don't provide in such cases valid Input/Output/ErrOutput variables (because IsConsole = false). But my streams below try to obtain standard stream handles under Windows regardless of IsConsole value. So even a GUI program is able to write to stdin/stdout/stderr using these streams.

  3. Unfortunately, in a GUI program under Windows you cannot depend on the fact that "StdOutStream <> nil means that stdout is actually available (because user redirected stdout etc.)". Reason ? Windows failure, as usual:

    This is tested on Windows 2000 Prof, with FPC 2.0.0 and 2.1.1 (revision 4317). When no stdout is available, StdOutStream should be nil, because GetStdHandle(STD_OUTPUT_HANDLE) should return 0. However, GetStdHandle(STD_OUTPUT_HANDLE) doesn't return 0... It returns some stupid handle (no, not INVALID_HANDLE_VALUE either) that you can't write into (trying to write returns in ERROR_INVALID_HANDLE WinAPI error). It seems that there is no way for me to check whether GetStdHandle(STD_OUTPUT_HANDLE) returned valid handle (e.g. because the program's stdout was redirected, so stdout is perfectly available) or whether it returned something unusable.

    So if you write an $apptype GUI program and you want to try to use stdout anyway, you can't just check for StdOutStream <> nil. You should also check the first write to StdOutStream for EWriteError.

    Note that GetStdHandle(STD_INPUT_HANDLE) and GetStdHandle(STD_ERROR_HANDLE) work correctly, so it should be OK to check StdInStream <> nil or StdErrStream <> nil. The only problematic one is GetStdHandle(STD_OUTPUT_HANDLE).

StdInReader: TTextReader;
 

Generated by PasDoc 0.10.0 on 2008-02-25 00:00:32