Unit KambiFilesUtils

DescriptionusesClasses, Interfaces, Objects and RecordsFunctions and ProceduresTypesConstantsVariables

Description

Operations on files.

Include functions to help cross-platform programs to know where to read/write files:

uses

Overview

Classes, Interfaces, Objects and Records

Name Description
Class EExeNameNotAvailable  
Class EFileExists  
Class EFileOpenError bezpieczne otwieranie plikow.

Functions and Procedures

function ExeName: string;
function ProgramName: string;
function NormalFileExists(const fileName: string): boolean;
function GetTempPath: string;
function UserConfigPath: string;
function UserConfigPath_Other(const WindowsExeNamePath: string): string;
function UserConfigFile(const FExtension: string): string;
function UserConfigFile_Other( const FExtension, UnixProgramName, WindowsExeName: string): string;
function UserConfigFile_FromProposed(const ProposedFileName: string): string;
function UserConfigFile_FromProposed_Other( const ProposedFileName, WindowsExeNamePath: string): string;
function ProgramDataPath: string;
function ProgramDataPath_Other( const UnixProgramName, WindowsExeNamePath: string): string;
function IsSymLink(const fname: string): boolean; overload;
function HomePath: string;
function ExpandHomePath(const fname: string): string;
function ExpandLongFilename(const fname: string): string;
procedure CheckDeleteFile(const FileName: string);
procedure CheckRemoveDir(const DirFileName: string);
procedure FileCopy(const SourceFname, DestFname: string; CanOverwrite: boolean);
function TryFileCopy(const SourceFname, DestFname: string; CanOverwrite: boolean): boolean;
procedure FileMove(const SourceFname, DestFname: string; CanOverwrite: boolean =False); overload;
procedure ChangeDir(const NewDir: string);
function FnameAutoInc(const fname_pattern: string): string;
function ParentPath(DirName: string; DoExpandDirName: boolean = true ): string;
procedure SafeReset(var f: file; const filename: string; readonly: boolean; opensize: word =1); overload;
procedure SafeReset(var f: text; const filename: string; readonly: boolean); overload;
procedure SafeRewrite(var f: file; const filename: string; opensize: word =1); overload;
procedure SafeRewrite(var f: text; const filename: string); overload;
function CombinePaths(BasePath, RelPath: string): string;

Description

Functions and Procedures

function ExeName: string;

Returns full (absolute) filename to executable file of this program. If it's impossible to obtain, raises exception EExeNameNotAvailable.

Under Windows this is simply ParamStr(0) (and it never raises exception), but under other OSes it's not so simple to obtain (although it's important to note that usually programs under UNIX should not need this, actually).

Internal implementation notes:

Under UNIXes other than Linux I don't know how to obtain this, so e.g. under FreeBSD this will always raise an exception. Under Linux I'm trying to read file /proc/getpid()/exe, this should work under most Linuxes as long as user compiled Linux kernel with /proc support. So under Linux this may work, but still you should be always prepared that it may raise EExeNameNotAvailable.

function ProgramName: string;

Returns *something like* basename of our executable file. This does not contain path and extension of our executable.

Under UNIXes this may be only the name of user-defined bash alias that was used to call our program, or user-defined symlink, or just pretty much anything that was passed to our program as argv[0].

Anyway, this is something that can be shown to user to identify our program. E.g. useful when you're writing error message on console, when UNIX standard is to always prefix error message with ProgramName + ': ', like 'cp: invalid file name'.

Right now this is simply equivalent to FPC's ApplicationName.

function NormalFileExists(const fileName: string): boolean;

Basically, returns true if file exists. So what's the difference between this and FileExists ? FileExists under Windows returns true for such names like 'con', 'c:\con', 'c:\somedir\con' itd. ('con' is a special device name). And this function returns false for them. For all other files (and other OSes) this function returns the same as FileExists.

function GetTempPath: string;

Returns directory suitable for creating temporary files/directories. Note that this directory is shared by other programs, so be careful when creating here anything – to minimize name conflicts usually all filenames created here should start with ProgramName and then should follow things like process id (or sometimes user name, when you can guarantee that one user runs always only one instance of this program) or some random number.

Also remember that good program should avoid creating temporary files – you should keep your content in memory, and store in it filesystem only when user really wants to. One exception to this rule is when program must cooperate with other program, sometimes the only possible (or stable, or fast) way to do this is by use of temporary files.

Always ends with trailing PathDelim.

function UserConfigPath: string;

Returns path that program should use to store user configuration files. This is some directory that is supposed to be writeable and that is a standard directory under this OS to put user config files.

E.g.

- under Windows with PlatformId = VER_PLATFORM_WIN32_NT (see GetVersionEx docs, VER_PLATFORM_WIN32_NT means Windows Server 2003, Windows XP, Windows 2000, or Windows NT") it tries to use SHGetSpecialFolderPath(0, @Path, CSIDL_APPDATA, true) This should return something like C:\Documents and Settings\<user-name>\Application Data ("Application Data" is localized, e.g. it's "Dane aplikacji" on Polish Windowses)

If that fails (because SHGetSpecialFolderPath is not available in shell32.dll (this can happen on Windows NT without Internet Explorer 4.0) or for some other reason) it falls back on ExtractFilePath(ExeName)

- under other Windowses (this includes Windows 95, 98, Millenium) it returns ExtractFilePath(ExeName)

- under UNIXes it's user's home directory

Always returns absolute (not relative) path. Result contains trailing PathDelim.

function UserConfigPath_Other(const WindowsExeNamePath: string): string;
 
function UserConfigFile(const FExtension: string): string;

Returns a filename that program should use to store it's configuration. This returns absolute filename that: - is inside UserConfigPath - has extention FExtension (FExtension should, as always, contain beginning dot. E.g. FExtension = '.ini'. This way you can pass FExtension = '' to have a filename without extension) - filename depends on ProgramName

This is equivalent to UserConfigFile_FromProposed(ProgramName + FExtension)

function UserConfigFile_Other( const FExtension, UnixProgramName, WindowsExeName: string): string;
 
function UserConfigFile_FromProposed(const ProposedFileName: string): string;

Returns abslute file name: - inside UserConfigPath - with FileName somehow derived from ProposedFileName

E.g. - under UNIXes, this is UserConfigPath + '.' + ProposedFileName - under Windows, this is UserConfigPath + ProposedFileName

function UserConfigFile_FromProposed_Other( const ProposedFileName, WindowsExeNamePath: string): string;
 
function ProgramDataPath: string;

Returns path that program should use to obtain installed data files. Returns absolute path, containing trailing PathDelim.

Here are details:

(Note that in normal circumstances such details are treated as internal implementation notes that shouldn't be exposed... But in case of this function, they must be exposed, since user and programmer must know how this function works (and usually it should be described in documentation of a program).

Under Windows : returns ExtractFilePath(ExeName).

Under UNIXes: returns HomePath +'.' +ProgramName+'.data/' if such directory exists, else returns '/usr/local/share/' +ProgramName+ '/'.

Note that HomePath +'.' +ProgramName +'.data/' is checked first, this allows user to override system-wide installation of my program with his own installation. E.g. consider the situation when an old version of my program is installed system-wide in /usr/local/share/my_program/, but some user (with no access to root account) wants to install a newer version of it for himself. Now he can do it, because ˜/.my_program.data/ is checked 1st, system-wide /usr/local/share/my_program/ is used only if ˜/.my_program.data/ does not exist.

function ProgramDataPath_Other( const UnixProgramName, WindowsExeNamePath: string): string;

Under Windows WindowsExeNamePath must be ExtractFilePath(ExeName) where ExeName is what ExeName would return for this "other" program.

Under UNIX UnixProgramName must be what ProgramName would return for this "other" program.

This way under UNIX parameter WindowsExeNamePath is ignored and under Windows parameter UnixProgramName is ignored. I know that this looks strange, but this is the only safe way to write interface of this procedure.

Use version without "_Other" suffix to get ProgramDataPath of *this* program, that uses this function.

function IsSymLink(const fname: string): boolean; overload;

czy fname to symbolic link ?

function HomePath: string;

User's home directory, with trailing PathDelim.

Taken from $HOME, unless $HOME = '' or is not defined, then I'm trying to take this from user-database by real-uid. This is what bash does (more-or-less, when home directory does not exist strange things happen), that's what programs should do according to `info libc' and my (Kambi's) preferences.

function ExpandHomePath(const fname: string): string;

Pod UNIXem rozwijanie ˜ to rzecz powloki. Procedury z libc, kernela, a za nimi procedury w System czy SysUtils nie rozumieja sciezek z nazwa ˜.

Co gorsza ˜ to pod Linuxem poprawna nazwa pliku, nawet jesli wiele programow wariuje widzac takie nazwy plikow.

Ponizsza funkcja przeprowadza rozwijanie ˜ na poczatku sciezki do ExclPathDelim(HomePath).

Pod windowsem ta funkcja nic nie robi, zwraca filename.

function ExpandLongFilename(const fname: string): string;

fname to nazwa pliku, moze byc wzgledna. Niniejsza funkcja zwraca to samo co ExpandFilename a wiec sciezke absolutna. Pod Windowsem dodatkowo gwarantowane jest ze KAZDY KOMPONENT tej sciezki bedzie mial nazwe dluga (nie DOSowa). Wiec ta funkcja nie tylko rozwija sciezke, ale sprawia ze jest zawsze napisana z uzyciem long-filenames.

procedure CheckDeleteFile(const FileName: string);

Wykonuje SysUtils.DeleteFile i sprawdza czy sie udalo, jesli nie - exception

procedure CheckRemoveDir(const DirFileName: string);

Podobnie jak CheckDeleteFile – wywoluje RemoveDir(DirFileName) i jesli sie nie udalo – exception z sensownym komunikatem

procedure FileCopy(const SourceFname, DestFname: string; CanOverwrite: boolean);

kopiuj Source na Dest plik, w razie bledu - exception. Jesli not CanOverwrite to i plik istnieje wyrzuca exception EFileExists, jesli CanOverwrite to robi overwrite pliku dest jesli juz istnial.

TODO: niech FileCopy kopiuje tez directory

function TryFileCopy(const SourceFname, DestFname: string; CanOverwrite: boolean): boolean;

j.w. ale zwraca false zamiast rzucac exception jesli sie nie udalo

procedure FileMove(const SourceFname, DestFname: string; CanOverwrite: boolean =False); overload;

move/rename file. Wersja bezpieczna - pozwala przenosic zarowno pliki jak i katalogi pomiedzy roznymi dyskami. W miare mozliwosci sprobuje wykonac normalne MoveFile/Rename ale jesli Dest jest na innym file-system niz Source (pod UNIXem spowoduje to blad procedury rename(), pod win spowoduje to blad w MoveFile() jezeli SourceFname to katalog) to wykona Copy(source,dest)+Delete(source).

Jesli DestFname istnieje to : jesli CanOverwrite to nadpisuje go jesli not CanOverwrite to wyrzuca EFileExists. W razie bledu - exception.

Uwagi : pod Linuxem z GNU libc sytuacja gdy SourceFname i DestFname to dwie nazwy dla tego samego pliku (do tego samego i-node) jest niezdefiniowana – nie wiadomo co w zwiazku z tym zrobi libc.__rename, w zwiazku z czym nie wiadomo tez co zrobi moje FileMove. Patrz strona info libc o "Renaming Files". Nie wiem jak wyglada tu sytuacja gdy nie uzywam libc (USE_LIBC symbol not defined), but I will assume that this situation is also undefined.

TODO: przenoszenia katalogow nie zrobilem jeszcze (no, w pewnych przypadkach mogloby zadzialac ale to sliskie i niedodefiniowane teraz rzeczy). Na razie uzywaj FileMove tylko do plikow.

procedure ChangeDir(const NewDir: string);

ChangeDir is something like "an improved replacement for ChDir". Changes current directory to NewDir, raises EInOutError if this is not possible. In other words, it mimics ChDir when compiled with $I (IO checking) on.

NewDir may (but don't have to) triling PathDelim.

Improvements:

1. It fixes a bug in Delphi 6, in Delphi 6 ChDir when fails (and program is compiled in $I+) does not raise EInOutError (but it sets IOResult).

Z $I+ kompilator powinien generowac kod sprawdzajacy zmienna IOResult po wywolaniu ChDir i wywolujacy ew. EInOutError (tak jak robi np. po wywolaniu Rewrite i w ogole po wywolaniu czegokolwiek o czym wie ze moze zwrocic InOutError).

Ale Delphi 6 nie robi tego - zwykle przeoczenie ? Uzywaj ChangeDir.

2. Better error message (that will always contain NewDir).

function FnameAutoInc(const fname_pattern: string): string;

FnameAutoInc robi Format(fname_pattern, [i]) zwiekszajac i od 0 az skonstruuje nazwe pliku ktory nie istnieje. Slowem, niech fname bedzie czyms w rodzaju "save_screen_%d.bmp" - FnameAutoInc z takim parametrem podstawi za %d najmniejsza liczbe nieujemna taka ze nie istnieje plik o tej nazwie.

function ParentPath(DirName: string; DoExpandDirName: boolean = true ): string;

Zwroc katalog nadrzedny do katalogu DirName. DirName moze byc absolutna sciezka, ale nie musi. Dirname moze ale nie musi zawierac koncowego PathDelim.

Jesli nie ma katalogu nadrzednego - zwroc DirName. Wynik zawsze jest expanded (absolutna sciezka) i zawsze zawiera koncowe PathDelim.

When DoExpandDirName then it is assumed that DirName already IS absolute path. What's the advantage of using DoExpandDirName = false ? Then this function is pure string-operation (no actual reading of any filesystem info), i.e. it belongs to KambiUtils_filenames.inc instead of KambiUtils_files.inc. This means that 1. Things work faster 2. DirName does not need to exist

procedure SafeReset(var f: file; const filename: string; readonly: boolean; opensize: word =1); overload;
 
procedure SafeReset(var f: text; const filename: string; readonly: boolean); overload;
 
procedure SafeRewrite(var f: file; const filename: string; opensize: word =1); overload;
 
procedure SafeRewrite(var f: text; const filename: string); overload;
 
function CombinePaths(BasePath, RelPath: string): string;

Combines BasePath with RelPath. BasePath MUST be an absolute path, on Windows it must contain at least drive specifier (like 'c:'), on Unix it must begin with "/". RelPath can be relative and can be absolute. If RelPath is absolute, result is RelPath. Else the result is an absolute path calculated by combining RelPath with BasePath.


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