| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569 | {    Copyright (c) 2013-2016 by Free Pascal development team    This unit implements the loading and searching of package files    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 fpcp;{$i fpcdefs.inc}interface  uses    cclasses,cstreams,    globtype,    pcp,finput,fpkg;  type    tpcppackage=class(tpackage)    private      loaded : boolean;      pcpfile : tpcpfile;    private      function openpcp:boolean;      function search_package(ashortname:boolean):boolean;      function search_package_file:boolean;      procedure setfilename(const fn:string;allowoutput:boolean);      procedure writecontainernames;      procedure writecontainedunits;      procedure writerequiredpackages;      procedure writepputable;      procedure writeppudata;      procedure readcontainernames;      procedure readcontainedunits;      procedure readrequiredpackages;      procedure readpputable;    public      constructor create(const pn:string);      destructor destroy; override;      procedure loadpcp;      procedure savepcp;      function getmodulestream(module:tmodulebase):tcstream;      procedure initmoduleinfo(module:tmodulebase);      procedure addunit(module:tmodulebase);      procedure add_required_package(pkg:tpackage);    end;implementation  uses    sysutils,    cfileutl,cutils,    systems,globals,version,    verbose,    ppu,    entfile,pkgutil;{ tpcppackage }  function tpcppackage.openpcp: boolean;    var      pcpfiletime : longint;    begin      result:=false;      Message1(package_t_pcp_loading,pcpfilename);      { Get pcpfile time (also check if the file exists) }      pcpfiletime:=getnamedfiletime(pcpfilename);      if pcpfiletime=-1 then       exit;    { Open the pcpfile }      Message1(package_u_pcp_name,pcpfilename);      pcpfile:=tpcpfile.create(pcpfilename);      if not pcpfile.openfile then       begin         pcpfile.free;         pcpfile:=nil;         Message(package_u_pcp_file_too_short);         exit;       end;    { check for a valid PPU file }      if not pcpfile.checkpcpid then       begin         pcpfile.free;         pcpfile:=nil;         Message(package_u_pcp_invalid_header);         exit;       end;    { check for allowed PCP versions }      if not (pcpfile.getversion=CurrentPCPVersion) then       begin         Message1(package_u_pcp_invalid_version,tostr(pcpfile.getversion));         pcpfile.free;         pcpfile:=nil;         exit;       end;    { check the target processor }      if tsystemcpu(pcpfile.header.common.cpu)<>target_cpu then       begin         pcpfile.free;         pcpfile:=nil;         Message(package_u_pcp_invalid_processor);         exit;       end;    { check target }      if tsystem(pcpfile.header.common.target)<>target_info.system then       begin         pcpfile.free;         pcpfile:=nil;         Message(package_u_pcp_invalid_target);         exit;       end;  {$ifdef cpufpemu}     { check if floating point emulation is on?       fpu emulation isn't unit levelwise because it affects calling convention }     if ((uf_fpu_emulation and pcpfile.header.common.flags)<>0) <>        (cs_fp_emulation in current_settings.moduleswitches) then       begin         pcpfile.free;         pcpfile:=nil;         Message(package_u_pcp_invalid_fpumode);         exit;       end;  {$endif cpufpemu}    { Show Debug info }      Message1(package_u_pcp_time,filetimestring(pcpfiletime));      Message1(package_u_pcp_flags,tostr(pcpfile.header.common.flags{flags}));      Message1(package_u_pcp_crc,hexstr(pcpfile.header.checksum,8));      (*Message1(package_u_pcp_crc,hexstr(ppufile.header.interface_checksum,8)+' (intfc)');      Message1(package_u_pcp_crc,hexstr(ppufile.header.indirect_checksum,8)+' (indc)');      Comment(V_used,'Number of definitions: '+tostr(ppufile.header.deflistsize));      Comment(V_used,'Number of symbols: '+tostr(ppufile.header.symlistsize));      do_compile:=false;*)      result:=true;    end;  function tpcppackage.search_package(ashortname:boolean):boolean;    var      singlepathstring,      filename : TCmdStr;    function package_exists(const ext:string;var foundfile:TCmdStr):boolean;      begin        if CheckVerbosity(V_Tried) then          Message1(package_t_packagesearch,Singlepathstring+filename+ext);        result:=FindFile(filename+ext,singlepathstring,true,foundfile);      end;    function package_search_path(const s:TCmdStr):boolean;      var        found : boolean;        hs    : TCmdStr;      begin        found:=false;        singlepathstring:=FixPath(s,false);        { Check for package file }        { TODO }        found:=package_exists({target_info.pkginfoext}'.pcp',hs);        if found then          begin            setfilename(hs,false);            found:=openpcp;          end;        result:=found;      end;    function search_path_list(list:TSearchPathList):boolean;      var        hp : TCmdStrListItem;        found : boolean;      begin        found:=false;        hp:=TCmdStrListItem(list.First);        while assigned(hp) do         begin           found:=package_search_path(hp.Str);           if found then            break;           hp:=TCmdStrListItem(hp.next);         end;        result:=found;      end;    begin      filename:=packagename^;      result:=search_path_list(packagesearchpath);    end;  function tpcppackage.search_package_file: boolean;    var      found : boolean;    begin      found:=false;      if search_package(false) then        found:=true;      if not found and          (length(packagename^)>8) and         search_package(true) then        found:=true;      result:=found;    end;  procedure tpcppackage.setfilename(const fn:string;allowoutput:boolean);    var      p,n : tpathstr;    begin      p:=FixPath(ExtractFilePath(fn),false);      n:=FixFileName(ChangeFileExt(ExtractFileName(fn),''));      { pcp name }      if allowoutput then        if (OutputUnitDir<>'') then          p:=OutputUnitDir        else          if (OutputExeDir<>'') then            p:=OutputExeDir;      pcpfilename:=p+n+{target_info.pkginfoext}'.pcp';    end;  procedure tpcppackage.writecontainernames;    begin      pcpfile.putstring(pplfilename);      //pcpfile.putstring(ppafilename);      pcpfile.writeentry(ibpackagefiles);    end;  procedure tpcppackage.writecontainedunits;    var      p : pcontainedunit;      i : longint;    begin      pcpfile.putlongint(containedmodules.count);      pcpfile.writeentry(ibstartcontained);      { for now we write the unit name and the ppu file name }      for i:=0 to containedmodules.count-1 do        begin          p:=pcontainedunit(containedmodules.items[i]);          pcpfile.putstring(p^.module.modulename^);          pcpfile.putstring(p^.ppufile);        end;      pcpfile.writeentry(ibendcontained);    end;  procedure tpcppackage.writerequiredpackages;    var      i : longint;    begin      pcpfile.putlongint(requiredpackages.count);      pcpfile.writeentry(ibstartrequireds);      for i:=0 to requiredpackages.count-1 do        begin          pcpfile.putstring(requiredpackages.NameOfIndex(i));        end;      pcpfile.writeentry(ibendrequireds);    end;  procedure tpcppackage.writepputable;    var      module : pcontainedunit;      i : longint;    begin      { no need to write the count again; it's the same as for the contained units }      for i:=0 to containedmodules.count-1 do        begin          module:=pcontainedunit(containedmodules[i]);          pcpfile.putlongint(module^.offset);          pcpfile.putlongint(module^.size);        end;      pcpfile.writeentry(ibpputable);    end;  procedure tpcppackage.writeppudata;    const      align: array[0..15] of byte = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);    var      i,      pos,      rem : longint;      module : pcontainedunit;      stream : TCStream;    begin      pcpfile.flush;      for i:=0 to containedmodules.count-1 do        begin          module:=pcontainedunit(containedmodules[i]);          pos:=pcpfile.position;          { align to 16 byte so that it can be nicely viewed in hex editors;            maybe we could also use 512 byte alignment instead }          rem:=$f-(pos and $f);          pcpfile.stream.write(align[0],rem+1);          pcpfile.flush;          module^.offset:=pcpfile.position;          { retrieve substream for the current position }          stream:=pcpfile.substream(module^.offset,-1);          rewriteppu(module^.module.ppufilename,stream);          module^.size:=stream.position;          stream.free;        end;      pos:=pcpfile.position;      { align to 16 byte so that it can be nicely viewed in hex editors;        maybe we could also use 512 byte alignment instead }      rem:=$f-(pos and $f);      pcpfile.stream.write(align[0],rem+1);    end;  procedure tpcppackage.readcontainernames;    begin      if pcpfile.readentry<>ibpackagefiles then        begin          message(package_f_pcp_read_error);          internalerror(2020100818);        end;      pplfilename:=pcpfile.getstring;      message1(package_u_ppl_filename,pplfilename);    end;  procedure tpcppackage.readcontainedunits;    var      cnt,i : longint;      name,path : string;      p : pcontainedunit;    begin      if pcpfile.readentry<>ibstartcontained then        begin          message(package_f_pcp_read_error);          internalerror(2020100819);        end;      cnt:=pcpfile.getlongint;      if pcpfile.readentry<>ibendcontained then        begin          message(package_f_pcp_read_error);          internalerror(2020100820);        end;      for i:=0 to cnt-1 do        begin          name:=pcpfile.getstring;          path:=pcpfile.getstring;          new(p);          p^.module:=nil;          p^.ppufile:=path;          p^.offset:=0;          p^.size:=0;          containedmodules.add(name,p);          message1(package_u_contained_unit,name);        end;    end;  procedure tpcppackage.readrequiredpackages;    var      cnt,i : longint;      name : string;    begin      if pcpfile.readentry<>ibstartrequireds then        begin          message(package_f_pcp_read_error);          internalerror(2014110901);        end;      cnt:=pcpfile.getlongint;      if pcpfile.readentry<>ibendrequireds then        begin          message(package_f_pcp_read_error);          internalerror(2014110902);        end;      for i:=0 to cnt-1 do        begin          name:=pcpfile.getstring;          requiredpackages.add(name,nil);          message1(package_u_required_package,name);        end;    end;  procedure tpcppackage.readpputable;    var      module : pcontainedunit;      i : longint;    begin      if pcpfile.readentry<>ibpputable then        begin          message(package_f_pcp_read_error);          internalerror(2015103001);        end;      for i:=0 to containedmodules.count-1 do        begin          module:=pcontainedunit(containedmodules[i]);          module^.offset:=pcpfile.getlongint;          module^.size:=pcpfile.getlongint;        end;    end;    constructor tpcppackage.create(const pn: string);    begin      inherited create(pn);      setfilename(pn+'.ppk',true);    end;  destructor tpcppackage.destroy;    begin      pcpfile.free;      inherited destroy;    end;  procedure tpcppackage.loadpcp;    var      newpackagename : string;    begin      if loaded then        exit;      if not search_package_file then        begin          Message1(package_f_cant_find_pcp,realpackagename^);          exit;        end      else        Message1(package_u_pcp_found,realpackagename^);      if not assigned(pcpfile) then        internalerror(2013053101);      if pcpfile.readentry<>ibpackagename then        Message1(package_f_cant_read_pcp,realpackagename^);      newpackagename:=pcpfile.getstring;      if upper(newpackagename)<>packagename^ then        Comment(V_Error,'Package was renamed: '+realpackagename^);      readcontainernames;      readrequiredpackages;      readcontainedunits;      readpputable;    end;  procedure tpcppackage.savepcp;    var      tablepos,      oldpos : longint;    begin      { create new ppufile }      pcpfile:=tpcpfile.create(pcpfilename);      if not pcpfile.createfile then        Message2(package_f_cant_create_pcp,realpackagename^,pcpfilename);      pcpfile.putstring(realpackagename^);      pcpfile.writeentry(ibpackagename);      writecontainernames;      writerequiredpackages;      writecontainedunits;      { the offsets and the contents of the ppus are not crc'd }      pcpfile.do_crc:=false;      pcpfile.flush;      tablepos:=pcpfile.position;      { this will write a table with empty entries }      writepputable;      pcpfile.do_crc:=true;      { the last entry ibend is written automatically }      { flush to be sure }      pcpfile.flush;      { create and write header }      pcpfile.header.common.size:=pcpfile.size;      pcpfile.header.checksum:=pcpfile.crc;      pcpfile.header.common.compiler:=wordversion;      pcpfile.header.common.cpu:=word(target_cpu);      pcpfile.header.common.target:=word(target_info.system);      //pcpfile.header.flags:=flags;      pcpfile.header.ppulistsize:=containedmodules.count;      pcpfile.header.requiredlistsize:=requiredpackages.count;      pcpfile.writeheader;      { write the ppu table which will also fill the offsets/sizes }      writeppudata;      pcpfile.flush;      oldpos:=pcpfile.position;      { now write the filled PPU table at the previously stored position }      pcpfile.position:=tablepos;      writepputable;      pcpfile.position:=oldpos;      { save crc in current module also }      //crc:=pcpfile.crc;      pcpfile.closefile;      pcpfile.free;      pcpfile:=nil;    end;  function tpcppackage.getmodulestream(module:tmodulebase):tcstream;    var      i : longint;      contained : pcontainedunit;    begin      for i:=0 to containedmodules.count-1 do        begin          contained:=pcontainedunit(containedmodules[i]);          if contained^.module=module then            begin              result:=pcpfile.substream(contained^.offset,contained^.size);              exit;            end;        end;      result:=nil;    end;  procedure tpcppackage.initmoduleinfo(module: tmodulebase);    begin      pplfilename:=extractfilename(module.sharedlibfilename);    end;  procedure tpcppackage.addunit(module: tmodulebase);    var      containedunit : pcontainedunit;    begin      new(containedunit);      containedunit^.module:=module;      containedunit^.ppufile:=extractfilename(module.ppufilename);      containedunit^.offset:=0;      containedunit^.size:=0;      containedmodules.add(module.modulename^,containedunit);    end;  procedure tpcppackage.add_required_package(pkg:tpackage);    var      p : tpackage;    begin      p:=tpackage(requiredpackages.find(pkg.packagename^));      if not assigned(p) then        requiredpackages.Add(pkg.packagename^,pkg)      else        if p<>pkg then          internalerror(2015112302);    end;end.
 |