{ This file is part of the Free Pascal run time library. Copyright (c) 1993-2015 by Florian Klaempfl member of the Free Pascal development team This is the install program for the DOS and OS/2 versions of Free Pascal See the file COPYING.FPC, included in this distribution, for details about the copyright. 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. **********************************************************************} program install; { $DEFINE DLL} (* TH - if defined, UNZIP32.DLL library is used to unpack. *) { $DEFINE DOSSTUB} (* TH - should _not_ be defined unless creating a bound DOS and OS/2 installer!!! *) (* Defining DOSSTUB causes adding a small piece of code *) (* for starting the OS/2 part from the DOS part of a bound *) (* application if running in OS/2 VDM (DOS) window. Used *) (* only if compiling with TP/BP (see conditionals below). *) {$IFDEF OS2} {$DEFINE DLL} {$ENDIF DLL} {$IFDEF VER60} {$DEFINE TP} {$ENDIF} {$IFDEF VER70} {$DEFINE TP} {$ENDIF} {$IFNDEF TP} {$UNDEF DOSSTUB} {$ELSE} {$IFDEF OS2} {$UNDEF DOSSTUB} {$ENDIF} {$ENDIF} {$IFDEF DPMI} {$UNDEF DOSSTUB} {$ENDIF} {$ifdef go32v2} {$define MAYBE_LFN} {$endif} {$ifdef debug} {$ifdef win32} {$define MAYBE_LFN} {$endif win32} {$endif debug} {$ifdef TP} {$define MAYBE_LFN} {$endif} uses {$IFDEF OS2} {$IFDEF FPC} DosCalls, {$ELSE FPC} {$IFDEF VirtualPascal} OS2Base, {$ELSE VirtualPascal} BseDos, {$ENDIF VirtualPascal} {$ENDIF FPC} {$ENDIF OS2} {$IFDEF GO32V2} emu387, {$ENDIF} {$ifdef HEAPTRC} heaptrc, {$endif HEAPTRC} strings,dos,objects,drivers, {$IFNDEF FVISION} commands, HelpCtx, {$ENDIF} unzip51g,ziptypes, {$IFDEF DLL} unzipdll, {$ENDIF} app,dialogs,views,menus,msgbox,colortxt,tabs,scroll, WHTMLScn,insthelp; const installerversion='3.3.1'; installercopyright='Copyright (c) 1993-2018 Florian Klaempfl'; maxpacks=20; maxpackages=32; maxdefcfgs=1024; HTMLIndexExt = '.htx'; CfgExt = '.dat'; MaxStatusPos = 4; StatusChars: string [MaxStatusPos] = '/-\|'; StatusPos: byte = 1; { this variable is set to true if an ide is installed } haside : boolean = false; hashtmlhelp : boolean = false; {$ifdef Unix} DirSep='/'; {$else} DirSep='\'; {$endif} type tpackage=record name : string[60]; zip : string[40]; { default zipname } zipshort : string[12]; { 8.3 zipname } diskspace : int64; { diskspace required } end; tpack=record name : string[12]; binsub : string[40]; ppc386 : string[20]; targetname : string[40]; defidecfgfile, defideinifile, defcfgfile, setpathfile : string[12]; include : boolean; { filechk : string[40]; Obsolete } packages : longint; package : array[1..maxpackages] of tpackage; end; tcfgarray = array[1..maxdefcfgs] of pstring; cfgrec=record title : string[80]; version : string[20]; helpidx, docsub, basepath : DirStr; packs : word; pack : array[1..maxpacks] of tpack; defideinis, defidecfgs, defcfgs, defsetpaths : longint; defideini, defidecfg, defcfg, defsetpath : tcfgarray; end; datarec=record basepath : DirStr; cfgval : word; packmask : array[1..maxpacks] of sw_word; end; punzipdialog=^tunzipdialog; tunzipdialog=object(tdialog) filetext : pstatictext; extractfiletext : pstatictext; currentfile : string; constructor Init(var Bounds: TRect; ATitle: TTitleStr); procedure do_unzip(s,topath:string); end; penddialog = ^tenddialog; tenddialog = object(tdialog) constructor init; end; pinstalldialog = ^tinstalldialog; tinstalldialog = object(tdialog) constructor init; procedure handleevent(var event : tevent);virtual; end; PFPHTMLFileLinkScanner = ^TFPHTMLFileLinkScanner; TFPHTMLFileLinkScanner = object(THTMLFileLinkScanner) function CheckURL(const URL: string): boolean; virtual; function CheckText(const Text: string): boolean; virtual; procedure ProcessDoc(Doc: PHTMLLinkScanFile); virtual; end; phtmlindexdialog = ^thtmlindexdialog; thtmlindexdialog = object(tdialog) text : pstatictext; constructor init(var Bounds: TRect; ATitle: TTitleStr); end; tapp = object(tapplication) procedure initmenubar;virtual; procedure initstatusline;virtual; procedure handleevent(var event : tevent);virtual; procedure do_installdialog; procedure readcfg(const fn:string); procedure checkavailpack; end; PSpecialInputLine= ^TSpecialInputLine; TSpecialInputLine = object (TInputLine) procedure GetData(var Rec); virtual; end; {$IFDEF DOSSTUB} PByte = ^byte; PRunBlock = ^TRunBlock; TRunBlock = record Length: word; Dependent: word; Background: word; TraceLevel: word; PrgTitle: PChar; PrgName: PChar; Args: PChar; TermQ: longint; Environment: pointer; Inheritance: word; SesType: word; Icon: pointer; PgmHandle: longint; PgmControl: word; Column: word; Row: word; Width: word; Height: word; end; {$ENDIF} var installapp : tapp; startpath : string; successfull : boolean; cfg : cfgrec; data : datarec; CfgName: NameStr; DStr: DirStr; EStr: ExtStr; UnzDlg : punzipdialog; log : text; createlog : boolean; {$IFNDEF DLL} const UnzipErr: longint = 0; {$ENDIF} {$ifdef MAYBE_LFN} const locallfnsupport : boolean = false; {$endif MAYBE_LFN} {***************************************************************************** Helpers *****************************************************************************} procedure errorhalt; begin installapp.done; if CreateLog then begin WriteLn (Log, 'Installation hasn''t been completed.'); Close (Log); end; halt(1); end; procedure WriteLog (const S: string); begin if CreateLog then begin WriteLn (Log, S); Flush (Log); end; end; function packagemask(i:longint):longint; begin packagemask:=1 shl (i-1); end; function upper(const s : string):string; var i : integer; begin for i:=1 to length(s) do if s[i] in ['a'..'z'] then upper[i]:=chr(ord(s[i])-32) else upper[i]:=s[i]; upper[0]:=s[0]; end; procedure Replace(var s:string;const s1,s2:string); var i : longint; begin repeat i:=pos(s1,s); if i>0 then begin Delete(s,i,length(s1)); Insert(s2,s,i); end; until i=0; end; function DotStr(l:longint):string; var TmpStr : string[32]; i : longint; begin Str(l,TmpStr); i:=Length(TmpStr); while (i>3) do begin i:=i-3; if TmpStr[i]<>'-' then Insert('.',TmpStr,i+1); end; DotStr:=TmpStr; end; function file_exists(const f : string;const path : string) : boolean; begin file_exists:=fsearch(f,path)<>''; end; function createdir(s:string):boolean; var s1,start : string; err : boolean; i : longint; begin err:=false; {$I-} getdir(0,start); {$ifndef Unix} if (s[2]=':') and (s[3]=DirSep) then begin chdir(Copy(s,1,3)); Delete(S,1,3); end; {$endif} repeat i:=Pos(DirSep,s); if i=0 then i:=255; s1:=Copy(s,1,i-1); Delete(s,1,i); ChDir(s1); if ioresult<>0 then begin mkdir(s1); chdir(s1); if ioresult<>0 then begin err:=true; break; end; end; until s=''; chdir(start); {$I+} createdir:=err; end; function DiskSpaceN(const zipfile : string) : longint; var compressed,uncompressed : longint; s : string; begin s:=zipfile+#0; if not (IsZip (@S [1])) then DiskSpaceN := -1 else begin Uncompressed:=UnzipSize(@s[1],compressed); DiskSpaceN:=uncompressed shr 10; end; end; function diskspacestr(uncompressed : longint) : string; begin if Uncompressed = -1 then DiskSpacestr := ' [INVALID]' else diskspacestr:=' ('+DotStr(uncompressed)+' KB)'; end; function createinstalldir(s : string) : boolean; var err : boolean; dir : searchrec; params : array[0..0] of pointer; begin if s[length(s)]=DirSep then dec(s[0]); FindFirst(s,AnyFile,dir); if doserror=0 then begin if Dir.Attr and Directory = 0 then begin messagebox('A file with the name chosen as the installation '+ 'directory exists already. Cannot create this directory!',nil, mferror+mfokbutton); createinstalldir:=false; end else createinstalldir:=messagebox('The installation directory exists already. '+ 'Do you want to continue ?',nil, mferror+mfyesbutton+mfnobutton)=cmYes; exit; end; err:=Createdir(s); if err then begin params[0]:=@s; messagebox('The installation directory %s couldn''t be created', @params,mferror+mfokbutton); createinstalldir:=false; exit; end; {$ifndef TP} {$IFNDEF OS2} FindClose (dir); {$ENDIF} {$endif} createinstalldir:=true; end; function GetProgDir: DirStr; var D: DirStr; N: NameStr; E: ExtStr; begin FSplit (FExpand (ParamStr (0)), D, N, E); if (D [0] <> #0) and (D [byte (D [0])] = '\') then Dec (D [0]); GetProgDir := D; end; function GetZipErrorInfo(error : longint) : string; var ErrorStr : string; begin case error of unzip_CRCErr : GetZipErrorInfo:='CRC error'; unzip_WriteErr : GetZipErrorInfo:='Write error'; unzip_ReadErr : GetZipErrorInfo:='Read error'; unzip_ZipFileErr : GetZipErrorInfo:='ZipFile erroe'; unzip_UserAbort : GetZipErrorInfo:='User abort'; unzip_NotSupported : GetZipErrorInfo:='Not supported'; unzip_Encrypted : GetZipErrorInfo:='File is encrypted'; unzip_InUse : GetZipErrorInfo:='Fie is in use'; unzip_InternalError : GetZipErrorInfo:='Internal error'; {Error in zip format} unzip_NoMoreItems : GetZipErrorInfo:='No more items'; unzip_FileError : GetZipErrorInfo:='File error'; {Error Accessing file} unzip_NotZipfile : GetZipErrorInfo:='Not a zipfile'; {not a zip file} unzip_SeriousError : GetZipErrorInfo:='Serious error'; {serious error} unzip_MissingParameter : GetZipErrorInfo:='Missing parameter'; {missing parameter} else begin Str(Error,ErrorStr); GetZipErrorInfo:='Unknown error '+errorstr; end; end; end; {***************************************************************************** HTML-Index Generation *****************************************************************************} var indexdlg : phtmlindexdialog; constructor thtmlindexdialog.Init(var Bounds: TRect; ATitle: TTitleStr); var r : trect; begin inherited init(bounds,atitle); Options:=Options or ofCentered; R.Assign (4, 2,bounds.B.X-Bounds.A.X-2, 4); text:=new(pstatictext,init(r,'Please wait ...')); insert(text); end; procedure TFPHTMLFileLinkScanner.ProcessDoc(Doc: PHTMLLinkScanFile); var oldtext : pstring; begin oldtext:=indexdlg^.text^.text; indexdlg^.text^.text:=newstr('Processing '+Doc^.GetDocumentURL); indexdlg^.text^.drawview; inherited ProcessDoc(Doc); disposestr(indexdlg^.text^.text); indexdlg^.text^.text:=oldtext; indexdlg^.text^.drawview; end; function TFPHTMLFileLinkScanner.CheckURL(const URL: string): boolean; var OK: boolean; const HTTPPrefix = 'http:'; FTPPrefix = 'ftp:'; begin OK:=inherited CheckURL(URL); if OK then OK:=DirAndNameOf(URL)<>''; if OK then OK:=CompareText(copy(ExtOf(URL),1,4),'.HTM')=0; if OK then OK:=CompareText(copy(URL,1,length(HTTPPrefix)),HTTPPrefix)<>0; if OK then OK:=CompareText(copy(URL,1,length(FTPPrefix)),FTPPrefix)<>0; CheckURL:=OK; end; function TFPHTMLFileLinkScanner.CheckText(const Text: string): boolean; var OK: boolean; S: string; begin S:=Trim(Text); OK:=(S<>'') and (copy(S,1,1)<>'['); CheckText:=OK; end; procedure writehlpindex(filename : string); var LS : PFPHTMLFileLinkScanner; BS : PBufStream; Re : Word; params : array[0..0] of pointer; dir : searchrec; r : trect; begin r.assign(10,10,70,15); indexdlg:=new(phtmlindexdialog,init(r,'Creating HTML index file, please wait ...')); desktop^.insert(indexdlg); { warning FIXME !!!!, don't know what is to fix here ... PM } New(LS, Init(DirOf(FileName))); LS^.ProcessDocument(FileName,[soSubDocsOnly]); if LS^.GetDocumentCount=0 then begin params[0]:=@filename; MessageBox('Problem creating help index %1, aborting',@params, mferror+mfokbutton); end else begin FileName:=DirAndNameOf(FileName)+HTMLIndexExt; findfirst(filename,AnyFile,dir); if doserror=0 then begin params[0]:=@filename; Re:=MessageBox('Help index %s already exists, overwrite it?',@params, mfinformation+mfyesbutton+mfnobutton); end else Re:=cmYes; if Re<>cmNo then begin New(BS, Init(FileName, stCreate, 4096)); if Assigned(BS)=false then begin MessageBox('Error while writing help index! '+ 'No help index is created',@params, mferror+mfokbutton); Re:=cmCancel; end else begin LS^.StoreDocuments(BS^); if BS^.Status<>stOK then begin MessageBox('Error while writing help index!'#13+ 'No help index is created',@params, mferror+mfokbutton); Re:=cmCancel; end; Dispose(BS, Done); end; end; end; Dispose(LS, Done); desktop^.delete(indexdlg); dispose(indexdlg,done); end; {***************************************************************************** Writing of fpc.cfg *****************************************************************************} procedure writedefcfg(const fn:string;const cfgdata : tcfgarray;count : longint;const targetname : string); var t : text; i : longint; s : string; dir : searchrec; params : array[0..0] of pointer; d : dirstr; n : namestr; e : extstr; begin { already exists } findfirst(fn,AnyFile,dir); if doserror=0 then begin params[0]:=@fn; if MessageBox('Config %s already exists, continue writing default config?',@params, mfinformation+mfyesbutton+mfnobutton)=cmNo then exit; end; { create directory } fsplit(fn,d,n,e); createdir(d); { create the fpc.cfg } assign(t,fn); {$I-} rewrite(t); {$I+} if ioresult<>0 then begin params[0]:=@fn; MessageBox(#3'A config not written.'#13#3'%s'#13#3'couldn''t be created',@params,mfinformation+mfokbutton); exit; end; for i:=1 to count do if assigned(cfgdata[i]) then begin s:=cfgdata[i]^; Replace(s,'%basepath%',data.basepath); Replace(s,'%targetname%',targetname); if pos('-',targetname)=0 then begin Replace(s,'%targetos%',targetname); Replace(s,'%fpctargetmacro%','$FPCOS') end else begin Replace(s,'%targetos%',Copy(targetname,pos('-',targetname)+1,255)); Replace(s,'%fpctargetmacro%','$FPCTARGET'); end; writeln(t,s); end else writeln(t,''); close(t); end; {***************************************************************************** TUnZipDialog *****************************************************************************} constructor tunzipdialog.Init(var Bounds: TRect; ATitle: TTitleStr); var r : trect; begin inherited init(bounds,atitle); Options:=Options or ofCentered; (* R.Assign (11, 4, 38, 6);*) R.Assign (1, 4,bounds.B.X-Bounds.A.X-2, 6); filetext:=new(pstatictext,init(r,#3'File: ')); insert(filetext); R.Assign (1, 7,bounds.B.X-Bounds.A.X-2, 9); extractfiletext:=new(pstatictext,init(r,#3' ')); insert(extractfiletext); end; {$IFNDEF DLL} procedure UnzipCheckFn (Retcode: longint; Rec: pReportRec );{$ifdef Delphi32}STDCALL;{$endif} {$ifndef fpc}{$IFNDEF BIT32} FAR;{$ENDIF BIT32}{$endif} var name : string; begin case Rec^.Status of unzip_starting: UnzipErr := 0; file_starting: begin with UnzDlg^.extractfiletext^ do begin Disposestr(text); name:=Strpas(Rec^.FileName); UnzDlg^.currentfile:=name; Text:=NewStr(#3+name); DrawView; end; end; file_failure: UnzipErr := RetCode; file_unzipping: begin with UnzDlg^.FileText^ do begin Inc (StatusPos); if StatusPos > MaxStatusPos then StatusPos := 1; Text^ [Length (Text^)] := StatusChars [StatusPos]; DrawView; end; end; end; end; {$ENDIF} procedure tunzipdialog.do_unzip(s,topath : string); var {$ifdef MAYBE_LFN} p : pathstr; n : namestr; e : extstr; islfn : boolean; {$endif MAYBE_LFN} again : boolean; st2,fn,dir,wild : string; begin Disposestr(filetext^.text); filetext^.Text:=NewStr(#3'File: '+s + #13#3' '); filetext^.drawview; if not(file_exists(s,startpath)) then begin messagebox('File "'+s+'" missing for the selected installation. '+ 'Installation hasn''t been completed.',nil,mferror+mfokbutton); WriteLog ('File "' + S + '" missing for the selected installation!'); errorhalt; end; {$IFNDEF DLL} {$IFDEF FPC} SetUnzipReportProc (@UnzipCheckFn); {$ELSE FPC} SetUnzipReportProc (UnzipCheckFn); {$ENDIF FPC} {$ENDIF DLL} WriteLog ('Unpacking ' + AllFiles + ' from ' + StartPath + DirSep + S + ' to ' + ToPath); repeat fn:=startpath+DirSep+s+#0; dir:=topath+#0; wild:=AllFiles + #0; again:=false; FileUnzipEx(@fn[1],@dir[1],@wild[1]); if (UnzipErr <> 0) and (UnzipErr <> 1) then begin if CreateLog then begin WriteLn (Log, 'Error ', UnzipErr, ' while unpacking!'); Flush (Log); end; s:=GetZipErrorInfo(UnzipErr); { Str(UnzipErr,s);} st2:=''; if UnzipErr=unzip_WriteErr then begin {$ifdef MAYBE_LFN} if not(locallfnsupport) then begin islfn:=false; fsplit(currentfile,p,n,e); if (length(n)>8) or (length(e)>4) or (pos('.',n)>0) or (upper(p+n+e)<>upper(currentfile)) then islfn:=true; if islfn then begin WriteLog ('Error while extracting ' + CurrentFile + ' because of missing LFN support,' + LineEnding + ' skipping rest of ZIP file.'); messagebox('Error while extracting '+currentfile+ #13#3'because of missing lfn support'+ #13#3'skipping rest of zipfile '+s ,nil,mferror+mfOkButton); again:=false; exit; end; end else {$endif MAYBE_LFN} st2:=' Disk full?'; end; if CreateLog then WriteLog ('Error (' + S + ') while extracting.' + ST2); if messagebox('Error (' + S + ') while extracting.'+st2+#13+ #13#3'Try again?',nil,mferror+mfyesbutton+mfnobutton)=cmYes then again:=true else errorhalt; end; until not again; end; {***************************************************************************** TEndDialog *****************************************************************************} constructor tenddialog.init; var R : TRect; P : PStaticText; Control : PButton; YB: word; {$IFNDEF UNIX} i : longint; S: string; WPath: boolean; MixedCasePath: boolean; {$ENDIF} {$IFDEF OS2} ErrPath: array [0..259] of char; Handle: longint; WLibPath: boolean; const EMXName: array [1..4] of char = 'EMX'#0; BFD2EName: array [1..6] of char = 'BFD2E'#0; {$ENDIF} begin if haside then YB := 15 else YB := 14; {$IFNDEF UNIX} s:=''; for i:=1 to cfg.packs do if cfg.pack[i].binsub<>'' then begin if s<>'' then s:=s+';'; S := s+Data.BasePath + Cfg.pack[i].BinSub; end; if Pos (Upper (S), Upper (GetEnv ('PATH'))) = 0 then begin WPath := true; Inc (YB, 3); end else WPath := false; { look if path is set as Path, this leads to problems for mingw32 make PM } MixedCasePath:=false; for i:=1 to EnvCount do begin if Pos('PATH=',Upper(EnvStr(i)))=1 then if Pos('PATH=',EnvStr(i))<>1 then Begin MixedCasePath:=true; Inc(YB, 2); End; end; {$IFDEF OS2} if DosLoadModule (@ErrPath, SizeOf (ErrPath), @EMXName, Handle) = 0 then begin WLibPath := false; DosFreeModule (Handle); if DosLoadModule (@ErrPath, SizeOf (ErrPath), @BFD2EName, Handle) = 0 then begin WLibPath := false; DosFreeModule (Handle); end else begin WLibPath := true; Inc (YB, 2); end; end else begin WLibPath := true; Inc (YB, 2); end; {$ENDIF} {$ENDIF} R.Assign(6, 6, 74, YB); inherited init(r,'Installation successful.'); Options:=Options or ofCentered; {$IFNDEF UNIX} if WPath then begin R.Assign(2, 3, 64, 5); P:=new(pstatictext,init(r,'Extend your PATH variable with '''+S+'''')); insert(P); end; {$IFDEF OS2} if WLibPath then begin if WPath then S := 'and your LIBPATH with ''' + S else S := 'Extend your LIBPATH with ''' + S; System.Delete (S, Length (S) - 6, 7); S := S + 'dll'''; R.Assign (2, YB - 15, 64, YB - 13); P := New (PStaticText, Init (R, S)); Insert (P); end; {$ELSE OS2} if MixedCasePath then begin R.Assign(2, 5, 64, 6); P:=new(pstatictext,init(r,'You need to use setpath.bat file if you want to use Makefiles')); insert(P); end; {$ENDIF OS2} {$ENDIF} R.Assign(2, YB - 13, 64, YB - 12); P:=new(pstatictext,init(r,'To compile files enter ''fpc [file]''')); insert(P); if haside then begin R.Assign(2, YB - 12, 64, YB - 10); P:=new(pstatictext,init(r,'To start the IDE (Integrated Development Environment) type ''fp'' at a command line prompt')); insert(P); end; R.Assign (29, YB - 9, 39, YB - 7); Control := New (PButton, Init (R,'~O~k', cmOK, bfDefault)); Insert (Control); end; {***************************************************************************** TInstallDialog *****************************************************************************} {$ifdef MAYBE_LFN} var islfn : boolean; procedure lfnreport( Retcode : longint;Rec : pReportRec ); var p : pathstr; n : namestr; e : extstr; begin fsplit(strpas(rec^.Filename),p,n,e); if (length(n)>8) or (length(e)>4) or (pos('.',n)>0) or (upper(p+n+e)<>upper(strpas(rec^.Filename))) then islfn:=true; end; function haslfn(const zipfile : string) : boolean; var buf : array[0..255] of char; begin strpcopy(buf,zipfile); islfn:=false; {$ifdef FPC} ViewZip(buf,AllFiles,@lfnreport); {$else FPC} ViewZip(buf,AllFiles,lfnreport); {$endif FPC} haslfn:=islfn; end; {$endif MAYBE_LFN} var AllFilesPresent : boolean; procedure presentreport( Retcode : longint;Rec : pReportRec ); var st : string; f : file; size,time : longint; p : pathstr; n : namestr; e : extstr; begin if not ALLFilesPresent then exit; st:=Data.BasePath+strpas(rec^.Filename); fsplit(st,p,n,e); if not file_exists(n+e,p) then AllFilesPresent:=false else begin Assign(f,st); Reset(f,1); if IOresult<>0 then begin ALLfilesPresent:=false; exit; end; GetFtime(f,time); size:=FileSize(f); if (rec^.Time<>time) or (rec^.size<>size) then ALLFilesPresent:=false; close(f); end; end; function AreAllFilesPresent(const zipfile : string) : boolean; var buf : array[0..255] of char; begin strpcopy(buf,zipfile); AllFilesPresent:=true; {$ifdef FPC} ViewZip(buf,AllFiles,@presentreport); {$else FPC} ViewZip(buf,AllFiles,presentreport); {$endif FPC} AreAllFilesPresent:=AllFilesPresent; end; constructor tinstalldialog.init; const width = 76; height = 20; x1 = (79-width) div 2; y1 = (23-height) div 2; x2 = x1+width; y2 = y1+height; var tabr,tabir,r : trect; packmask : array[1..maxpacks] of longint; enabmask : array[1..maxpacks] of longint; i,line,j : integer; items : array[1..maxpacks] of psitem; f : pview; found : boolean; okbut,cancelbut : pbutton; firstitem : array[1..maxpacks] of integer; packcbs : array[1..maxpacks] of pcheckboxes; packtd : ptabdef; labpath : plabel; ilpath : pspecialinputline; tab : ptab; titletext : pcoloredtext; labcfg : plabel; cfgcb : pcheckboxes; scrollbox: pscrollbox; sbr,sbsbr: trect; sbsb: pscrollbar; zipfile : string; begin f:=nil; { walk packages reverse and insert a newsitem for each, and set the mask } for j:=1 to cfg.packs do with cfg.pack[j] do begin firstitem[j]:=0; items[j]:=nil; packmask[j]:=0; enabmask[j]:=0; for i:=packages downto 1 do begin zipfile:=''; if file_exists(package[i].zip,startpath) then zipfile:=startpath+DirSep+package[i].zip else if file_exists(package[i].zipshort,startpath) then begin zipfile:=startpath+DirSep+package[i].zipshort; { update package to replace the full zipname with the short name } package[i].zip:=package[i].zipshort; end; if zipfile<>'' then begin { get diskspace required } package[i].diskspace:=diskspaceN(zipfile); {$ifdef MAYBE_LFN} if not(locallfnsupport) then begin if not(haslfn(zipfile)) then begin items[j]:=newsitem(package[i].name+diskspacestr(package[i].diskspace),items[j]); packmask[j]:=packmask[j] or packagemask(i); enabmask[j]:=enabmask[j] or packagemask(i); firstitem[j]:=i-1; WriteLog ('Checking lfn usage for ' + zipfile + ' ... no lfn'); end else begin items[j]:=newsitem(package[i].name+' (requires LFN support)',items[j]); enabmask[j]:=enabmask[j] or packagemask(i); firstitem[j]:=i-1; WriteLog ('Checking lfn usage for ' + zipfile + ' ... uses lfn'); end; end else {$endif MAYBE_LFN} begin items[j]:=newsitem(package[i].name+diskspacestr(package[i].diskspace) {$ifdef DEBUG} +' ('+dotstr(i)+')' {$endif DEBUG} ,items[j]); packmask[j]:=packmask[j] or packagemask(i); enabmask[j]:=enabmask[j] or packagemask(i); firstitem[j]:=i-1; end; end else items[j]:=newsitem(package[i].name {$ifdef DEBUG} +' ('+dotstr(i)+')' {$endif DEBUG} ,items[j]); end; end; { If no component found abort } found:=false; for j:=1 to cfg.packs do if packmask[j]<>0 then found:=true; if not found then begin messagebox('No components found to install, aborting.',nil,mferror+mfokbutton); if CreateLog then WriteLog ('No components found to install, aborting.'); errorhalt; end; r.assign(x1,y1,x2,y2); inherited init(r,''); Options:=Options or ofCentered; GetExtent(R); R.Grow(-2,-1); Dec(R.B.Y,2); TabR.Copy(R); TabIR.Copy(R); TabIR.Grow(-2,-2); TabIR.Move(-2,0); {-------- General Sheets ----------} R.Copy(TabIR); r.move(0,1); r.b.x:=r.a.x+40; r.b.y:=r.a.y+1; new(titletext,init(r,cfg.title,$71)); r.move(0,2); r.b.x:=r.a.x+40; new(labpath,init(r,'~B~ase path',f)); r.move(0,1); r.b.x:=r.a.x+40; r.b.y:=r.a.y+1; new(ilpath,init(r,high(DirStr))); r.move(0,2); r.b.x:=r.a.x+40; new(labcfg,init(r,'Con~f~ig',f)); r.move(0,1); r.b.x:=r.a.x+40; r.b.y:=r.a.y+1; new(cfgcb,init(r,newsitem('create fpc.cfg',nil))); data.cfgval:=1; {-------- Pack Sheets ----------} for j:=1 to cfg.packs do begin R.Copy(TabIR); if R.A.Y+cfg.pack[j].packages>R.B.Y then R.B.Y:=R.A.Y+cfg.pack[j].packages; new(packcbs[j],init(r,items[j])); if data.packmask[j]=high(sw_word) then data.packmask[j]:=packmask[j]; packcbs[j]^.enablemask:={$ifdef DEV}$7fffffff{$else}enabmask[j]{$endif}; packcbs[j]^.sel:=firstitem[j]; end; {--------- Main ---------} packtd:=nil; sbr.assign(1,3,tabr.b.x-tabr.a.x-3,tabr.b.y-tabr.a.y-1); for j:=cfg.packs downto 1 do begin if (sbr.b.y-sbr.a.y)'' then writedefcfg(data.basepath+cfg.pack[i].binsub+DirSep+cfg.pack[i].defcfgfile,cfg.defcfg,cfg.defcfgs,cfg.pack[i].targetname); if cfg.pack[i].setpathfile<>'' then writedefcfg(data.basepath+cfg.pack[i].binsub+DirSep+cfg.pack[i].setpathfile,cfg.defsetpath,cfg.defsetpaths,cfg.pack[i].targetname); end; if haside then begin for i:=1 to cfg.packs do if cfg.pack[i].defidecfgfile<>'' then writedefcfg(data.basepath+cfg.pack[i].binsub+DirSep+cfg.pack[i].defidecfgfile,cfg.defidecfg,cfg.defidecfgs,cfg.pack[i].targetname); for i:=1 to cfg.packs do if cfg.pack[i].defideinifile<>'' then writedefcfg(data.basepath+cfg.pack[i].binsub+DirSep+cfg.pack[i].defideinifile,cfg.defideini,cfg.defideinis,cfg.pack[i].targetname); if hashtmlhelp then writehlpindex(data.basepath+DirSep+cfg.DocSub+DirSep+cfg.helpidx); end; end; begin data.basepath:=cfg.basepath; data.cfgval:=0; for j:=1 to cfg.packs do data.packmask[j]:=high(sw_word); repeat { select components } p:=new(pinstalldialog,init); c:=executedialog(p,@data); if (c=cmok) then begin if Data.BasePath = '' then messagebox('Please, choose the directory for installation first.',nil,mferror+mfokbutton) else begin Data.BasePath := FExpand (Data.BasePath); if Data.BasePath [Length (Data.BasePath)] = DirSep then Dec (Data.BasePath [0]); found:=false; for j:=1 to cfg.packs do if data.packmask[j]>0 then found:=true; if found then begin {$IFNDEF UNIX} { TH - check the available disk space here } DSize := 0; for j:=1 to cfg.packs do with cfg.pack[j] do begin for i:=1 to packages do begin if data.packmask[j] and packagemask(i)<>0 then begin ASpace := package[i].diskspace; if ASpace = -1 then begin MessageBox ('File ' + package[i].zip + ' is probably corrupted!', nil, mferror + mfokbutton); WriteLog ('File ' + package[i].zip + ' is probably corrupted!'); end else Inc (DSize, ASpace); end; end; end; WriteLog ('Diskspace needed: ' + DotStr (DSize) + ' Kb'); S := Data.BasePath; Space := DiskFree (byte (Upcase(S [1])) - 64); { -1 means that the drive is invalid } if Space=-1 then begin WriteLog ('The drive ' + S [1] + ': is not valid'); if messagebox('The drive '+S[1]+': is not valid. Do you ' + 'want to change the installation path?',nil, mferror+mfyesbutton+mfnobutton) = cmYes then Continue; Space:=0; end; Space := Space shr 10; WriteLog ('Free space on drive ' + S [1] + ': ' + DotStr (Space) + ' Kb'); if Space < DSize then S := 'is not ' else S := ''; if (Space < DSize + 500) then begin if S = '' then S := 'might not be '; if messagebox('There ' + S + 'enough space on the target ' + 'drive for all the selected components. Do you ' + 'want to change the installation path?',nil, mferror+mfyesbutton+mfnobutton) = cmYes then Continue; end; {$ENDIF} if createinstalldir(data.basepath) then break; end else begin { maybe only config } if (data.cfgval and 1)<>0 then begin result:=messagebox('No components selected.'#13#13'Create a configfile ?',nil, mfinformation+mfyesbutton+mfnobutton); if (result=cmYes) and createinstalldir(data.basepath) then doconfigwrite; exit; end else begin result:=messagebox('No components selected.'#13#13'Abort installation?',nil, mferror+mfyesbutton+mfnobutton); if result=cmYes then exit; end; end; end; end else exit; until false; { extract packages } for j:=1 to cfg.packs do with cfg.pack[j] do begin r.assign(10,7,70,18); UnzDlg:=new(punzipdialog,init(r,'Extracting Packages')); desktop^.insert(UnzDlg); for i:=1 to packages do begin if data.packmask[j] and packagemask(i)<>0 then begin UnzDlg^.do_unzip(package[i].zip,data.basepath); { gather some information about the installed files } if copy(package[i].zip,1,3)='ide' then haside:=true; if copy(package[i].zip,1,7)='doc-htm' then begin hashtmlhelp:=true; { correct the fpctoc file name if .html files are used } if package[i].zip='doc-html.zip' then if copy(cfg.helpidx,length(cfg.helpidx)-3,4)='.htm' then cfg.helpidx:=cfg.helpidx+'l'; end; end; end; desktop^.delete(UnzDlg); dispose(UnzDlg,done); end; { write config } if (data.cfgval and 1)<>0 then doconfigwrite; { show end message } p3:=new(penddialog,init); executedialog(p3,nil); end; procedure tapp.readcfg(const fn:string); var t : text; i,j,k, line : longint; item, s,hs : string; params : array[0..0] of pointer; {$ifndef FPC} procedure readln(var t:text;var s:string); var c : char; i : longint; begin c:=#0; i:=0; while (not eof(t)) and (c<>#10) do begin read(t,c); if c<>#10 then begin inc(i); s[i]:=c; end; end; if (i>0) and (s[i]=#13) then dec(i); s[0]:=chr(i); end; {$endif} begin assign(t,StartPath + DirSep + fn); {$I-} reset(t); {$I+} if ioresult<>0 then begin StartPath := GetProgDir; assign(t,StartPath + DirSep + fn); {$I-} reset(t); {$I+} if ioresult<>0 then begin params[0]:=@fn; messagebox('File %s not found!',@params,mferror+mfokbutton); WriteLog ('File "' + fn + '" not found!'); errorhalt; end; end; line:=0; while not eof(t) do begin readln(t,s); inc(line); if (s<>'') and not(s[1] in ['#',';']) then begin i:=pos('=',s); if i>0 then begin item:=upper(Copy(s,1,i-1)); system.delete(s,1,i); if item='VERSION' then cfg.version:=s else if item='TITLE' then cfg.title:=s else if item='BASEPATH' then cfg.basepath:=s else if item='HELPIDX' then cfg.helpidx:=s else if item='DOCSUB' then cfg.docsub:=s else if item='DEFAULTCFG' then begin repeat readln(t,s); if upper(s)='ENDCFG' then break; if cfg.defcfgsmaxpacks then begin MessageBox ('Too many packs!', nil, mfError + mfOkButton); if CreateLog then begin WriteLn (Log, 'Too many packs'); close(log); end; halt(1); end; cfg.pack[cfg.packs].name:=s; end else if item='CFGFILE' then begin if cfg.packs=0 then begin MessageBox ('No pack set found!', nil, mfError + mfOkButton); if CreateLog then begin WriteLn (Log, 'No pack set'); close(Log); end; halt(1); end; cfg.pack[cfg.packs].defcfgfile:=s end else if item='IDECFGFILE' then begin if cfg.packs=0 then begin MessageBox ('No pack set found!', nil, mfError + mfOkButton); if CreateLog then begin WriteLn (Log, 'No pack set'); Close(Log); end; halt(1); end; cfg.pack[cfg.packs].defidecfgfile:=s end else if item='SETPATHFILE' then begin if cfg.packs=0 then begin MessageBox ('No pack set found!', nil, mfError + mfOkButton); if CreateLog then begin WriteLn (Log, 'No pack set'); close(Log); end; halt(1); end; cfg.pack[cfg.packs].setpathfile:=s end else if item='IDEINIFILE' then begin if cfg.packs=0 then begin MessageBox ('No pack set found!', nil, mfError + mfOkButton); if CreateLog then begin WriteLn (Log, 'No pack set'); Close(Log); end; halt(1); end; cfg.pack[cfg.packs].defideinifile:=s end else if item='PPC386' then begin if cfg.packs=0 then begin MessageBox ('No pack set found!', nil, mfError + mfOkButton); if CreateLog then begin WriteLn (Log, 'No pack set'); Close(Log); end; halt(1); end; cfg.pack[cfg.packs].ppc386:=s; end else if item='BINSUB' then begin if cfg.packs=0 then begin MessageBox ('No pack set found!', nil, mfError + mfOkButton); if CreateLog then begin WriteLn (Log, 'No pack set'); Close(Log); end; halt(1); end; cfg.pack[cfg.packs].binsub:=s; end {else: Obsolete PM } { if item='FILECHECK' then begin if cfg.packs=0 then begin MessageBox ('No pack set found!', nil, mfError + mfOkButton); if CreateLog then WriteLn (Log, 'No pack set'); halt(1); end; cfg.pack[cfg.packs].filechk:=s; end } else if item='TARGETNAME' then begin if cfg.packs=0 then begin MessageBox ('No pack set found!', nil, mfError + mfOkButton); if CreateLog then begin WriteLn (Log, 'No pack set'); Close(Log); end; halt(1); end; cfg.pack[cfg.packs].targetname:=s; end else if item='PACKAGE' then begin if cfg.packs=0 then begin MessageBox ('No pack set found!', nil, mfError + mfOkButton); if CreateLog then begin WriteLn (Log, 'No pack set'); Close(Log); end; halt(1); end; with cfg.pack[cfg.packs] do begin j:=pos(',',s); if (j>0) and (packages0) then begin package[packages].zip:=Copy(hs,1,k-1); package[packages].zipshort:=Copy(hs,k+1,length(hs)-k-1); end else package[packages].zip:=hs; package[packages].name:=copy(s,j+1,255); end; package[packages].diskspace:=-1; end; end end; end; end; close(t); end; procedure tapp.checkavailpack; var i, j : longint; one_found : boolean; begin { check the packages } j:=0; while (j'' then} for i:=1 to cfg.pack[j].packages do begin if file_exists(cfg.pack[j].package[i].zip,startpath) or file_exists(cfg.pack[j].package[i].zipshort,startpath) then begin one_found:=true; break; end; end; if not one_found then begin { remove the package } move(cfg.pack[j+1],cfg.pack[j],sizeof(tpack)*(cfg.packs-j)); dec(cfg.packs); dec(j); end; end; end; procedure tapp.initmenubar; var r : trect; begin getextent(r); r.b.y:=r.a.y+1; menubar:=new(pmenubar,init(r,newmenu( newsubmenu('Free Pascal Installer',hcnocontext,newmenu(nil ), nil)))); end; procedure tapp.initstatusline; var R: TRect; begin GetExtent(R); R.A.Y := R.B.Y - 1; //R.B.X := R.B.X - 2; New(StatusLine, Init(R, NewStatusDef(0, $EFFF,nil,nil ) ) ); end; procedure tapp.handleevent(var event : tevent); begin inherited handleevent(event); if event.what=evcommand then if event.command=cmstart then begin clearevent(event); do_installdialog; if successfull then begin event.what:=evcommand; event.command:=cmquit; handleevent(event); end; end; end; {$IFDEF DOSSTUB} function CheckOS2: boolean; var OwnName: PathStr; OwnDir: DirStr; Name: NameStr; Ext: ExtStr; DosV, W: word; P: PChar; const Title: string [15] = 'FPC Installer'#0; RunBlock: TRunBlock = (Length: $32; Dependent: 0; Background: 0; TraceLevel: 0; PrgTitle: @Title [1]; PrgName: nil; Args: nil; TermQ: 0; Environment: nil; Inheritance: 0; SesType: 2; Icon: nil; PgmHandle: 0; PgmControl: 2; Column: 0; Row: 0; Width: 80; Height: 25); begin CheckOS2 := false; asm mov ah, 30h int 21h xchg ah, al mov DosV, ax mov ax, 4010h int 2Fh cmp ax, 4010h jnz @0 xor bx, bx @0: mov W, bx end; if DosV > 3 shl 8 then begin OwnName := FExpand (ParamStr (0)); FSplit (OwnName, OwnDir, Name, Ext); if (DosV >= 20 shl 8 + 10) and (W >= 20 shl 8 + 10) then (* OS/2 version 2.1 or later running (double-checked) *) begin OwnName [Succ (byte (OwnName [0]))] := #0; RunBlock.PrgName := @OwnName [1]; P := Ptr (PrefixSeg, $80); if PByte (P)^ <> 0 then begin Inc (P); RunBlock.Args := Ptr (PrefixSeg, $81); end; asm mov ax, 6400h mov bx, 0025h mov cx, 636Ch mov si, offset RunBlock int 21h jc @0 mov DosV, 0 @0: end; CheckOS2 := DosV = 0; end; end; end; {$ENDIF} procedure usagescreen; begin writeln('FPC Installer ',installerversion,' ',installercopyright); writeln('Command line options:'); writeln(' -l create log file'); {$ifdef MAYBE_LFN} writeln(' --nolfn force installation with short file names'); {$endif MAYBE_LFN} writeln; writeln(' -h displays this help'); end; var OldExit: pointer; procedure NewExit; begin ExitProc := OldExit; if CreateLog then begin {$I-} if ErrorAddr <> nil then begin WriteLn (Log, 'Installer crashed with RTE ', ExitCode); Close (Log); end else if ExitCode <> 0 then begin WriteLn (Log, 'Installer ended with non-zero exit code ', ExitCode); Close (Log); end {$I+} end; end; var i : longint; { vm : tvideomode;} begin OldExit := ExitProc; ExitProc := @NewExit; { register objects for help streaming } RegisterWHTMLScan; {$IFDEF OS2} { TH - no error boxes if checking an inaccessible disk etc. } {$IFDEF FPC} DosCalls.DosError (0); {$ELSE FPC} {$IFDEF VirtualPascal} OS2Base.DosError (ferr_DisableHardErr); {$ELSE VirtualPascal} BseDos.DosError (0); {$ENDIF VirtualPascal} {$ENDIF FPC} {$ENDIF} {$IFDEF DOSSTUB} if CheckOS2 then Halt; {$ENDIF} createlog:=false; {$ifdef MAYBE_LFN} locallfnsupport:=system.lfnsupport; {$endif MAYBE_LFN} for i:=1 to paramcount do begin if paramstr(i)='-l' then createlog:=true {$ifdef MAYBE_LFN} else if paramstr(i)='--nolfn' then begin locallfnsupport:=false; {$ifdef GO32V2} { lfnsupport is a const in win32 RTL } system.lfnsupport:=locallfnsupport; {$endif GO32V2} end {$endif MAYBE_LFN} else if paramstr(i)='-h' then begin usagescreen; halt(0); end else begin usagescreen; halt(1); end; end; if createlog then begin assign(log,'install.log'); rewrite(log); {$ifdef MAYBE_LFN} if not(locallfnsupport) then WriteLog ('OS doesn''t have LFN support'); {$endif} end; getdir(0,startpath); successfull:=false; fillchar(cfg, SizeOf(cfg), 0); fillchar(data, SizeOf(data), 0); installapp.init; { vm.col:=80; vm.row:=25; vm.color:=true; installapp.SetScreenVideoMode(vm); } FSplit (FExpand (ParamStr (0)), DStr, CfgName, EStr); installapp.readcfg(CfgName + CfgExt); installapp.checkavailpack; { installapp.readcfg(startpath+dirsep+cfgfile);} {$ifdef GO32V2} if not(lfnsupport) then MessageBox('The operating system doesn''t support LFN (long file names),'+ ' so some packages will get shorten filenames when installed',nil,mfinformation or mfokbutton); {$endif} installapp.do_installdialog; installapp.done; if createlog then close(log); end.