[fpc-pascal] Unicodestrings and Assign(File)
Jürgen Hestermann
juergen.hestermann at gmx.de
Sat May 19 13:12:42 CEST 2012
> Given that, it doesn't really make sense to use the *A versions at all
> in Windows (Unless you want to support very old Windows versions).
> Thus the approach for Windows should be the opposite of Unix, I suppose.
> UTF8 or ANSI gets converted to UTF16 and call the *W functions.
That's true. In general the data type with the most information
(UnicodeString
in this case) should be used as much as possible and only when functions
require other (lower) data types a conversion should be done.
Otherwise Information gets lost.
> I have the same time problem, but I will take a look at the source code,
> I don't think it should be very difficult to figure out.
I had a look at the sources and found the following
(it's a bit longer, so if you are not interested just stop reading ;-)):
1.) The 2 fundamental file types filerec and textrec (for standard files and
text files) are defined in
fpc\fpc\rtl\inc\filerec.inc and
fpc\fpc\rtl\inc\textrec.inc
Both differ only by some buffer structures at the end but share the
first part
(including file name). The file name is of type
array[0..255] of char;
and needs to be replaced by UnicodeString.
This requires other changes:
1.) The order of elements in the file data structures have to be changed.
The name has to become the first element as in:
-----------------------------------------------------------
TextRec = Packed Record
name : UnicodeString;
Handle : THandle; // Handle has to be directly after
name, see Assign!
Mode : longint;
bufsize : SizeInt;
_private : SizeInt;
bufpos,
bufend : SizeInt;
bufptr : ^textbuf;
openfunc,
inoutfunc,
flushfunc,
closefunc : pointer;
UserData : array[1..32] of byte;
LineEnd : TLineEndStr;
buffer : textbuf;
{$ifdef FPC_HAS_CPSTRING}
CodePage : TSystemCodePage;
{$endif}
End;
-----------------------------------------------------------
Reason: See next item 2.)
2.) The Assign routine in \fpc\rtl\inc\file.inc overwrites the
whole file data structure with zeros (using fillchar). The
name has to be omitted from this action because it is now
managed automatically. So Assign needs to be changed to:
-----------------------------------------------------------
Procedure Assign(out f:File;const NewName:string);
{ Assign Name to file f so it can be used with the file routines }
Begin // Assign
FillChar(FileRec(f).Handle,SizeOf(FileRec)-Sizeof(FileRec(f).name),0);
FileRec(f).Handle := UnusedHandle;
FileRec(f).mode := fmClosed;
FileRec(f).Name := NewName;
End; // Assign
-----------------------------------------------------------
3.) The routine do_open in \fpc\rtl\win\sysfile.inc has to be
changed at 3 lines to (see "// THIS LINE CHANGED!")
-----------------------------------------------------------
procedure do_open(var f; p:UnicodeString; flags:longint); //
THIS LINE CHANGED!
{ filerec and textrec have both handle and mode as the first items so
they could use the same routine for opening/creating.
when (flags and $100) the file will be append
when (flags and $1000) the file will be truncate/rewritten
when (flags and $10000) there is no check for close (needed for
textfiles) }
Const file_Share_Read = $00000001;
file_Share_Write = $00000002;
file_Share_Delete = $00000004;
Var shflags,
oflags,cd : longint;
security : TSecurityAttributes;
begin // do_open
DoDirSeparators(p);
{ close first if opened }
if ((flags and $10000)=0) then
begin
case filerec(f).mode of
fminput,fmoutput,fminout :
Do_Close(filerec(f).handle);
fmclosed : ;
else {not assigned}
inoutres:=102;
exit;
end; // of case
end;
{ reset file handle }
filerec(f).handle := UnusedHandle;
{ convert filesharing }
shflags := 0;
if ((filemode and fmshareExclusive)=fmshareExclusive) then { no sharing }
else
if (filemode=fmShareCompat) or ((filemode and
fmshareDenyWrite)=fmshareDenyWrite) then
shflags := file_Share_Read
else
if ((filemode and fmshareDenyRead)=fmshareDenyRead) then
shflags := file_Share_Write
else
if ((filemode and fmshareDenyNone)=fmshareDenyNone) then
shflags := {$ifdef WINCE}
{ WinCE does not know file_share_delete }
file_Share_Read or file_Share_Write;
{$else WINCE}
fmShareDenyNoneFlags;
{$endif WINCE}
{ convert filemode to filerec modes }
case (flags and 3) of
0 : begin
filerec(f).mode := fminput;
oflags := longint(GENERIC_READ);
end;
1 : begin
filerec(f).mode := fmoutput;
oflags := longint(GENERIC_WRITE);
end;
2 : begin
filerec(f).mode := fminout;
oflags := longint(GENERIC_WRITE or GENERIC_READ);
end;
end; // of case
{ create it ? }
if (flags and $1000)<>0 then
cd:=CREATE_ALWAYS
{ or Append/Open ? }
else
cd:=OPEN_EXISTING;
{ empty name is special }
if p='' then // THIS LINE CHANGED!
begin
case FileRec(f).mode of
fminput :
FileRec(f).Handle := StdInputHandle;
fminout, { this is set by rewrite }
fmoutput :
FileRec(f).Handle := StdOutputHandle;
fmappend :
begin
FileRec(f).Handle := StdOutputHandle;
FileRec(f).mode := fmoutput; {fool fmappend}
end;
end; // of case
exit;
end;
security.nLength := Sizeof(TSecurityAttributes);
security.bInheritHandle := true;
security.lpSecurityDescriptor := nil;
filerec(f).handle :=
CreateFileW(pwidechar(p),oflags,shflags, at security,cd,FILE_ATTRIBUTE_NORMAL,0);
// THIS LINE CHANGED!
{ append mode }
if ((flags and $100)<>0) and
(filerec(f).handle<>0) and
(filerec(f).handle<>UnusedHandle) then
begin
do_seekend(filerec(f).handle);
filerec(f).mode:=fmoutput; {fool fmappend}
end;
{ get errors }
{ handle -1 is returned sometimes !! (PM) }
if (filerec(f).handle=0) or (filerec(f).handle=UnusedHandle) then
begin
errno:=GetLastError;
Errno2InoutRes;
end;
end; // do_open
-----------------------------------------------------------
3.) Of course, there are lots of places where the file data structures
filerec and textrec are used in the code. But in most cases only non-changed
record elements are used (so no changes are required) and only seldomly
the name is used/changed or the whole data structure is modified.
So I think the above modifications require to check a lot of code but
only a few modifications (hopefully). I would be willing to do these
changes but I am not sure whether I have overlooked something obvious.
These are very fundamental changes which affect all operating systems
so it may have unexpected side affects.
I also need to get familiar with SVN.
Any comments?
More information about the fpc-pascal
mailing list