123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469 |
- {
- Copyright (c) 1998-2002 by Peter Vreman
- This unit implements the message object
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- ****************************************************************************
- }
- unit cmsgs;
- {$i fpcdefs.inc}
- interface
- uses
- globtype;
- const
- maxmsgidxparts = 20;
- type
- ppchar=^pchar;
- TMsgStr = AnsiString;
- TArrayOfPChar = array[0..1000] of pchar;
- PArrayOfPChar = ^TArrayOfPChar;
- TArrayOfState = array[0..1000] of tmsgstate;
- PArrayOfState = ^TArrayOfState;
- PMessage=^TMessage;
- TMessage=object
- msgfilename : string;
- msgintern : boolean;
- msgallocsize,
- msgsize,
- msgparts,
- msgs : longint;
- msgtxt : pchar;
- msgidx : array[1..maxmsgidxparts] of PArrayOfPChar;
- msgidxmax : array[1..maxmsgidxparts] of longint;
- msgstates : array[1..maxmsgidxparts] of PArrayOfState;
- { set if changes with $WARN need to be cleared at next module change }
- has_local_changes : boolean;
- constructor Init(n:longint;const idxmax:array of longint);
- destructor Done;
- function LoadIntern(p:pointer;n:longint):boolean;
- function LoadExtern(const fn:string):boolean;
- procedure ClearIdx;
- procedure ResetStates;
- procedure CreateIdx;
- function GetPChar(nr:longint):pchar;
- { function ClearVerbosity(nr:longint):boolean; not used anymore }
- function SetVerbosity(nr:longint;newstate:tmsgstate):boolean;
- function Get(nr:longint;const args:array of TMsgStr):ansistring;
- end;
- { this will read a line until #10 or #0 and also increase p }
- function GetMsgLine(var p:pchar):string;
- implementation
- uses
- SysUtils,
- cutils;
- function MsgReplace(const s:TMsgStr;const args:array of TMsgStr):ansistring;
- var
- last,
- i : longint;
- hs : TMsgStr;
- begin
- if s='' then
- begin
- MsgReplace:='';
- exit;
- end;
- hs:='';
- i:=0;
- last:=0;
- while (i<length(s)-1) do
- begin
- inc(i);
- if (s[i]='$') and (s[i+1] in ['1'..'9']) then
- begin
- hs:=hs+copy(s,last+1,i-last-1)+args[byte(s[i+1])-byte('1')];
- inc(i);
- last:=i;
- end;
- end;
- MsgReplace:=hs+copy(s,last+1,length(s)-last);
- end;
- constructor TMessage.Init(n:longint;const idxmax:array of longint);
- var
- i,j : longint;
- begin
- msgtxt:=nil;
- has_local_changes:=false;
- msgsize:=0;
- msgparts:=n;
- if n<>high(idxmax)+1 then
- fail;
- for i:=1 to n do
- begin
- msgidxmax[i]:=idxmax[i-1];
- { create array of msgidx }
- getmem(msgidx[i],msgidxmax[i]*sizeof(pointer));
- fillchar(msgidx[i]^,msgidxmax[i]*sizeof(pointer),0);
- { create array of states }
- getmem(msgstates[i],msgidxmax[i]*sizeof(tmsgstate));
- { default value for msgstate is ms_on_global }
- for j:=0 to msgidxmax[i]-1 do
- msgstates[i]^[j]:=ms_on_global;
- end;
- end;
- destructor TMessage.Done;
- var
- i : longint;
- begin
- for i:=1 to msgparts do
- begin
- freemem(msgidx[i],msgidxmax[i]*sizeof(pointer));
- freemem(msgstates[i],msgidxmax[i]*sizeof(tmsgstate));
- end;
- if msgallocsize>0 then
- begin
- freemem(msgtxt,msgsize);
- msgallocsize:=0;
- end;
- msgtxt:=nil;
- msgsize:=0;
- msgparts:=0;
- end;
- function TMessage.LoadIntern(p:pointer;n:longint):boolean;
- begin
- msgtxt:=pchar(p);
- msgsize:=n;
- msgallocsize:=0;
- msgintern:=true;
- ClearIdx;
- CreateIdx;
- LoadIntern:=true;
- end;
- function TMessage.LoadExtern(const fn:string):boolean;
- const
- bufsize=8192;
- var
- f : text;
- error,multiline : boolean;
- line,i,j : longint;
- ptxt : pchar;
- s,s1 : string;
- buf : pointer;
- procedure err(const msgstr:TMsgStr);
- begin
- writeln('*** PPC, file ',fn,', error in line ',line,': ',msgstr);
- error:=true;
- end;
- begin
- LoadExtern:=false;
- getmem(buf,bufsize);
- { Read the message file }
- assign(f,fn);
- {$I-}
- reset(f);
- {$I+}
- if ioresult<>0 then
- begin
- WriteLn('*** PPC, can not open message file ',fn);
- exit;
- end;
- settextbuf(f,buf^,bufsize);
- { First parse the file and count bytes needed }
- error:=false;
- line:=0;
- multiline:=false;
- msgsize:=0;
- while not eof(f) do
- begin
- readln(f,s);
- inc(line);
- if multiline then
- begin
- if s=']' then
- multiline:=false
- else
- inc(msgsize,length(s)+1); { +1 for linebreak }
- end
- else
- begin
- if (s<>'') and not(s[1] in ['#',';','%']) then
- begin
- i:=pos('=',s);
- if i>0 then
- begin
- j:=i+1;
- if not(s[j] in ['0'..'9']) then
- err('no number found')
- else
- begin
- while (s[j] in ['0'..'9']) do
- inc(j);
- end;
- if j-i-1<>5 then
- err('number length is not 5');
- if s[j+1]='[' then
- begin
- inc(msgsize,j-i);
- multiline:=true
- end
- else
- inc(msgsize,length(s)-i+1);
- end
- else
- err('no = found');
- end;
- end;
- end;
- if multiline then
- err('still in multiline mode');
- if error then
- begin
- freemem(buf,bufsize);
- close(f);
- exit;
- end;
- { now read the buffer in mem }
- msgallocsize:=msgsize;
- getmem(msgtxt,msgallocsize);
- ptxt:=msgtxt;
- reset(f);
- while not eof(f) do
- begin
- readln(f,s);
- if multiline then
- begin
- if s=']' then
- begin
- multiline:=false;
- { overwrite last eol }
- dec(ptxt);
- ptxt^:=#0;
- inc(ptxt);
- end
- else
- begin
- move(s[1],ptxt^,length(s));
- inc(ptxt,length(s));
- ptxt^:=#10;
- inc(ptxt);
- end;
- end
- else
- begin
- if (s<>'') and not(s[1] in ['#',';','%']) then
- begin
- i:=pos('=',s);
- if i>0 then
- begin
- j:=i+1;
- while (s[j] in ['0'..'9']) do
- inc(j);
- { multiline start then no txt }
- if s[j+1]='[' then
- begin
- s1:=Copy(s,i+1,j-i);
- move(s1[1],ptxt^,length(s1));
- inc(ptxt,length(s1));
- multiline:=true;
- end
- else
- begin
- { txt including number }
- s1:=Copy(s,i+1,255);
- move(s1[1],ptxt^,length(s1));
- inc(ptxt,length(s1));
- ptxt^:=#0;
- inc(ptxt);
- end;
- end;
- end;
- end;
- end;
- close(f);
- freemem(buf,bufsize);
- { now we can create the index, clear if the previous load was also
- an external file, because those can't be reused }
- if not msgintern then
- ClearIdx;
- CreateIdx;
- { set that we've loaded an external file }
- msgintern:=false;
- LoadExtern:=true;
- end;
- procedure TMessage.ClearIdx;
- var
- i : longint;
- begin
- { clear }
- for i:=1 to msgparts do
- fillchar(msgidx[i]^,msgidxmax[i]*sizeof(pointer),0);
- end;
- procedure TMessage.CreateIdx;
- var
- hp1,
- hp,hpend : pchar;
- code : integer;
- num : longint;
- number : string[5];
- i : longint;
- numpart,numidx : longint;
- begin
- { process msgtxt buffer }
- number:='00000';
- hp:=msgtxt;
- hpend:=@msgtxt[msgsize];
- while (hp<hpend) do
- begin
- hp1:=hp;
- for i:=1 to 5 do
- begin
- number[i]:=hp1^;
- inc(hp1);
- end;
- val(number,num,code);
- numpart:=num div 1000;
- numidx:=num mod 1000;
- { check range }
- if (numpart <= msgparts) and (numidx < msgidxmax[numpart]) then
- begin
- { skip _ }
- inc(hp1);
- { put the address in the idx, the numbers are already checked }
- msgidx[numpart]^[numidx]:=hp1;
- end;
- { next string }
- hp:=pchar(@hp[strlen(hp)+1]);
- end;
- end;
- function GetMsgLine(var p:pchar):string;
- var
- i : longint;
- begin
- i:=0;
- while not(p^ in [#0,#10]) and (i<256) do
- begin
- inc(i);
- GetMsgLine[i]:=p^;
- inc(p);
- end;
- { skip #10 }
- if p^=#10 then
- inc(p);
- { if #0 then set p to nil }
- if p^=#0 then
- p:=nil;
- { return string }
- GetMsgLine[0]:=chr(i);
- end;
- function TMessage.GetPChar(nr:longint):pchar;
- begin
- if (nr div 1000 < msgparts) and
- (nr mod 1000 < msgidxmax[nr div 1000]) then
- GetPChar:=msgidx[nr div 1000]^[nr mod 1000]
- else
- GetPChar:='';
- end;
- function TMessage.SetVerbosity(nr:longint;newstate:tmsgstate):boolean;
- var
- i: longint;
- oldstate : tmsgstate;
- is_global : boolean;
- begin
- result:=false;
- i:=nr div 1000;
- if (i < low(msgstates)) or
- (i > msgparts) then
- exit;
- if (nr mod 1000 < msgidxmax[i]) then
- begin
- is_global:=(ord(newstate) and ms_global_mask) <> 0;
- oldstate:=msgstates[i]^[nr mod 1000];
- if not is_global then
- newstate:= tmsgstate((ord(newstate) and ms_local_mask) or (ord(oldstate) and ms_global_mask));
- if newstate<>oldstate then
- has_local_changes:=true;
- msgstates[i]^[nr mod 1000]:=newstate;
- result:=true;
- end;
- end;
- {
- function TMessage.ClearVerbosity(nr:longint):boolean;
- begin
- ClearVerbosity:=SetVerbosity(nr,ms_off);
- end;
- }
- function TMessage.Get(nr:longint;const args:array of TMsgStr):ansistring;
- var
- hp : pchar;
- begin
- if (nr div 1000 < msgparts) and
- (nr mod 1000 < msgidxmax[nr div 1000]) then
- hp:=msgidx[nr div 1000]^[nr mod 1000]
- else
- hp:=nil;
- if hp=nil then
- Get:='msg nr '+tostr(nr)
- else
- Get:=MsgReplace(system.strpas(hp),args);
- end;
- procedure TMessage.ResetStates;
- var
- i,j,glob : longint;
- state : tmsgstate;
- begin
- if not has_local_changes then
- exit;
- for i:=1 to msgparts do
- for j:=0 to msgidxmax[i] - 1 do
- begin
- state:=msgstates[i]^[j];
- glob:=(ord(state) and ms_global_mask) shr ms_shift;
- state:=tmsgstate((glob shl ms_shift) or glob);
- msgstates[i]^[j]:=state;
- end;
- has_local_changes:=false;
- end;
- end.
|