function [mcr_zipfile,mcrlist] = buildmcr(mcr_zipfile_dirname, ...
					  mcr_zipfile_filename, ...
                                          use_tmw_b_file, ...
                                          tmw_b_dirpath)
%BUILDMCR builds a ZIP file of the files required for the MCR.
%
%   MCR_ZIPFILE = BUILDMCR returns the path of the generated
%   ZIP file in MCR_ZIPFILE. Here MCR_ZIPFILE is:
%
%   <matlabroot>/toolbox/compiler/deploy/<arch>/MCRInstaller.zip 
%
%   MCR_ZIPFILE = BUILDMCR(MCR_ZIPFILE_DIRNAME) returns the
%   ZIP file in:
%
%       <MCR_ZIPFILE_DIRNAME>/MCRInstaller.zip
%
%   The directory is created as needed.
%
%   MCR_ZIPFILE = BUILDMCR(MCR_ZIPFILE_DIRNAME,MCR_ZIPFILE_FILENAME)
%   returns the ZIP file in:
%
%       <MCR_ZIPFILE_DIRNAME>/<MCR_ZIPFILE_FILENAME>
%
%   The directory is created as needed.
%
%   [MCR_ZIPFILE,MCRLIST] = BUILDMCR(...) returns the list of
%   the files in the ZIP file in MCRLIST. It is a cell array of
%   paths each relative to the matlabroot.
%
%   If the ZIP file already exists nothing is done and a warning
%   is produced.
%   
%   The required list is constructed from the installer files and
%   pruned appropriately.
%
%   Technically it is the transitive closure of dependencies for the
%   'mclmcr' shared library.
%
%   Example(s),
%
%     buildmcr;
%
%         builds 'MCRInstaller.zip' in the default directory.
%         returns nothing (unless an error or not the first time).
%
%     mcr_zipfile = buildmcr;
%
%         builds 'MCRInstaller.zip' in the default directory.
%         returns mcr_zipfile = ...
%             fullfile(matlabroot,'toolbox','compiler',
%		       'deploy',ARCH,'MCRInstaller.zip');
%
%     mcr_zipfile = buildmcr('.');
%
%         builds 'MCRInstaller.zip' in the current directory.
%         returns mcr_zipfile = fullfile(pwd,'MCRInstaller.zip')
%
%     mcr_zipfile = buildmcr('.','my.zip');
%
%         builds 'my.zip' in the current directory.
%         returns mcr_zipfile = fullfile(pwd,'my.zip')

%   Rajesh Godbole, Martin Knapp-Cordes, Peter Webb
%   Copyright 1984-2004 The MathWorks, Inc.
%   $Revision: 1.1.6.22.2.1 $ $Date: 2005/01/27 19:17:55 $
%---------------------------------------------------------------------

global mcrversion;

% Each release of the MCR has a unique version number. We use this version
% number to allow multiple versions of the MCR to co-exist on the same 
% machine. However, in order to do so, certain files that are ordinarily
% stored in bin/<arch> need to be moved into runtime/<arch>. 
%
% The mcrversion global constant contains information to help manage this
% process.

% Version number for THIS VERSION of the MCR.
mcrversion.major = '7';
mcrversion.minor = '2';

% This is the string prepended to the directory name where the MCR will
% be installed. All the paths in the ZIP file will start with this string.
mcrversion.string = ['v' mcrversion.major mcrversion.minor];

% Files that contain these patterns are moved from bin/<arch> to 
% runtime/<arch>.
mcrversion.unix = [ mcrversion.major '.' mcrversion.minor ];
mcrversion.pc = [ mcrversion.major mcrversion.minor ];

% Files that match certain other patterns (architecture specific) need to 
% be moved as well. This is platform specific.

% On Windows, move the Microsoft DLLs and non-versioned COM DLLs from from 
% bin/win32 to runtime/win32. Also, PrintImage.exe needs to be in 
% runtime/win32.
mcrversion.patterns.win32 = {
    'bin/.*mfc.*\.dll' ...
    'bin/.*msvc.*\.dll' ...
    'bin/.*/mwcommgr.dll' ...
    'bin/.*/mwcomutil.dll' ...
    'bin/.*/PrintImage.exe' ...
 };

% On UNIX systems, mclmcrrt.so needs to be in the runtime/<ARCH> directory
mcrversion.patterns.glnx86 = {
    'bin/glnx86/.*mclmcrrt.so$' ...
};

mcrversion.patterns.sol2 = {
    'bin/sol2/.*mclmcrrt.so$' ...
};

mcrversion.patterns.mac = {
    'bin/mac/.*mclmcrrt.so$' ...
};

mcrversion.patterns.hpux = {
    'bin/hpux/.*mclmcrrt.sl$' ...
};

mcrversion.patterns.glnxa64 = {
    'bin/glnxa64/.*mclmcrrt.so$' ...
};

% Check the number of input arguments
arg_error = nargchk(0,4,nargin);
if ~isempty(arg_error)
   error('COMPILER:BUILDMCR:NumberOfInputArguments',arg_error);
end

if nargin == 0
  mcr_zipfile_dirname = '';
  mcr_zipfile_filename = '';
  use_tmw_b_file = 0;
  tmw_b_dirpath = '';
elseif nargin == 1
  mcr_zipfile_filename = '';
  use_tmw_b_file = 0;
  tmw_b_dirpath = '';
elseif nargin == 2
  use_tmw_b_file = 0;
  tmw_b_dirpath = '';
elseif nargin == 3
  tmw_b_dirpath = '';
end

if nargout == 2
  mcrlist = {};
end

% Environment info
RootDir = matlabroot;
if ispc
    Arch = 'win32';
else
    Arch = lower(computer);
end

% Process the input directory name, to allow for the use of shell
% symbols like ~, and environment variables.
if (length(mcr_zipfile_dirname) > 0)
    mcr_zipfile_dirname = expand_dir_name(Arch, mcr_zipfile_dirname);
end

% Get pathname of zipfile. An empty pathname means an error
% was thrown in the routine and we should quit.
%
mcr_zipfile = get_mcr_zipfile(Arch,mcr_zipfile_dirname, ...
			      mcr_zipfile_filename);
if isempty(mcr_zipfile)
  return
end

if exist(mcr_zipfile,'file')
  warning('COMPILER:BUILDMCR:AlreadyBuilt',...
            'Zip file already built. Delete first to rebuild.')
  return
end

% prune_list: common for all platforms
prune_list = {'demos/.*';
	      'toolbox/compiler/.*';
	      '.*/EmacsLink/.*';
	      '.*/sys/jaws/.*';
	      'jhelp/.*';
              'extern/examples/.*';
              'extern/lib/.*';
              'help/.*';
              'notebook/.*';
              'sys/lcc/.*';
              'sys/namespace/.*';
              'toolbox/.*';
              '.*\.csf$'; 
              '.*\.exe$';
              '.*license.txt$';
             '.*/ja/.*';
	     'netbeans.jar';
             'java/jar/test/.*';
             'jhelp/.*';
             'bin/win32/.*opts.*';
             'java/extern/emacslink.*';
             'extern/src/.*';
             '.*\.tlb.*';
	     '.*\.phl';
             'bin/.*/.*qt-mt.*';
             'bin/.*/.*dastudio\..*';
             '.*\.hlp';
             '.*cmdwin.jar';
             '.*jhelp/.*';
             '.*/demo.lic$'};          
 
% prune_list on unix
prune_list_unix  = {'.*/MATLAB'; '1.0.1.0*';
                    'bin/mbuild.*';
                    'bin/mex.*';
                    'bin/.*/mcc.*';
                    'bin/.*/mlint.*'};

% prune_list on pc
prune_list_win32  = {'bin/win32/mex.*';
	             '.*/java/.*/msvcrt.dll';  % In the end, there can be
	             '.*/jade/.*/msvcrt.dll';  % only one. (matlab/bin/win32)
                     'bin/win32/mbuild.*';
                     'bin/win32/mcc.*';
                     'bin/win32/mlint.*';
                    '.*\.lnk$';
                    '^[a-zA-Z]:\\.*'; % to exclude all absolute
                                      % paths starting with drive
                                      % letter "C:\"
                    '^\\\\.*';        % to exclude all UNC paths
                    'bin/matlab.bat';
                    '.*matlab\..*'; % removes matlab.bat, .ico, .el. jdf, phl
                    '.*matlabaddin.*';
                    '.*matlabwizard.*'}; 

% additional_file_list common for all platforms
% The custom version of FigureMenuBar.fig and FigureToolBar.fig
% have been added explicitly to be distributed with the compiled
% application. 

additional_file_list = { ...
     'license.txt';
     fullfile('java', 'jar', 'toolbox', 'instrument.jar');
     fullfile('toolbox','local', 'classloader.txt');
     fullfile('toolbox','local', 'classpath.txt');
     fullfile('toolbox','local', 'librarypath.txt');
     fullfile('toolbox','compiler','deploy', 'FigureMenuBar.fig'); 
     fullfile( 'toolbox','compiler','deploy',  'FigureToolBar.fig') ...
};

% Include Ghostscript executable
if (ispc)
    additional_file_list{end+1} = ...
          fullfile('sys', 'ghostscript', 'bin', Arch, 'gs.exe');
else
    additional_file_list{end+1} = ...
          fullfile('sys', 'ghostscript', 'bin', Arch, 'gs');
end

% Additional files on Windows
additional_file_list_win32 = { ...
    'bin/win32/mwregsvr.exe'; ...
    'bin/win32/PrintImage.exe' ...
};

% Search for a further list of additional files. These files are specified
% by root directory and file name -- match_files will return all files 
% matching the file name pattern that reside in the directory hierarchy
% below the given root.

find_file_list_win32(1).root = fullfile(matlabroot, 'extern/lib');
find_file_list_win32(1).file = 'mclmcrrt.lib';

additional_file_list_win32 = [additional_file_list_win32 ; ...
                              match_files(find_file_list_win32)];

if (ispc)
    % The b-file uses / on all platforms, while the productdata.dat file 
    % uses \ on the PC.
    if (use_tmw_b_file == 0)
        prune_list = strrep(prune_list,'/','\\');
 	prune_list_win32 = strrep(prune_list_win32,'/','\\');
        mcrversion.patterns.win32 = ...
              strrep(mcrversion.patterns.win32, '/','\\');
	additional_file_list = strrep(additional_file_list,'/','\\');
	additional_file_list_win32 =  ...
              strrep(additional_file_list_win32,'/','\\');
    else
 	prune_list_win32 = strrep(prune_list_win32,'\\','/');
    end       
end

disp(['Building MCR installer file list for ' Arch]);
disp('...Getting list of shared libraries and other binaries.');

switch (Arch)
    
    case 'win32'
        % Read from I/P file 
        %     Mathworks: MATLABROOT/cdimages/win32/bfiles/matlab_b 
        %     Customer:  MATLABROOT/uninstall/productdata 

        if (use_tmw_b_file == 0)
            % First extract MATLABROOT/uninstall/productdata.dat from 
            %               MATLABROOT/uninstall/productdata by doing unzip(productdata)

            % extract to temporary directory so that deployment can be done
            % out of a read-only MATLAB install

            tmpDir = tempdir;
            if ~exist(tmpDir, 'file')
               tmpDir=tempname;
                mkdir(tmpDir);
                tmpDirCreated = true;
            else
                tmpDirCreated = false;
            end
    
            unzip(fullfile(RootDir,'uninstall','productdata'), tmpDir);
            [file_list] = pc_matlab_pathlist(fullfile(tmpDir,'productdata.dat'));
            delete(fullfile(tmpDir, 'productdata.dat'));
            if tmpDirCreated
                rmdir(tmpDir);
            end

        else % Use the TMW _b files

	    if (numel(tmw_b_dirpath) > 0)
	        bfileRootDir = tmw_b_dirpath;
            else
	        bfileRootDir = fullfile(matlabroot, 'cdimages', 'win32', 'bfiles');
	    end
	
            if (exist(bfileRootDir, 'file') ~= 7)
	         error(['MathWorks B-file location must be a directory ' ...
                       bfileRootDir ' is not a directory.']);
            end

	    % Need to read from MATLAB, Compiler, COM Builder and 
	    % Excel Builder
	    matlab_b_file = fullfile(bfileRootDir,'matlab_b');
	    compiler_b_file = fullfile(bfileRootDir,'compiler_b');
	    combuilder_b_file = fullfile(bfileRootDir,'combuilder_b');
	    excelbuilder_b_file = fullfile(bfileRootDir,'matlabxl_b');

	    str = sprintf('%s %s %s %s', read_all(matlab_b_file), ...
                                   read_all(compiler_b_file), ...
                                   read_all(combuilder_b_file), ...
                                   read_all(excelbuilder_b_file));

	    % Split the string at whitespaces to create the file list
	    file_list = strread(str, '%s');
        end

        file_list = prune_it(file_list,prune_list);      

        FileArray = [file_list; additional_file_list]; %column vector addition
        FileArray = prune_it(FileArray,prune_list_win32);      
        FileArray = [FileArray; additional_file_list_win32]; 

    otherwise % glnx86, sol2, hpux, mac
	bfileRootDir = matlabroot;

        if (exist(bfileRootDir, 'file') ~= 7)
	    error(['B-file location must be a directory ' bfileRootDir ' is not a directory.']);
        end

        % Read from I/P file MATLABROOT/cdimages/unix/update/pd/matlab/b
        [file_list1] = read_b_file(fullfile(bfileRootDir, ...
                                  'update/pd/matlab')); 
        file_list1 = prune_it(file_list1, prune_list);
        
        % Read from I/P file MATLABROOT/cdimages/unix/update/pd/matlab/<arch>/b
        [file_list2] = read_b_file(fullfile(bfileRootDir, ...
                                  'update/pd/matlab',Arch)); 
        file_list2 = prune_it(file_list2, prune_list);
    
        % Additional stuff needed for the compiler (mclmcr specific)
        % Read from I/P file 
        %    MATLABROOT/cdimages/unix/update/pd/toolbox/compiler/<arch>/b
        [file_list3] = read_b_file(fullfile(bfileRootDir, ...
                                  'update/pd/toolbox/compiler', ...
                                  Arch)); 
        file_list3 = prune_it(file_list3, prune_list);

	% The files shipping from toolbox/compiler. The only ones we
	% need are the headers placed into extern/include.
	%
        % Read from I/P file 
        %   MATLABROOT/cdimages/unix/update/pd/toolbox/compiler/b
        [file_list4] = read_b_file(fullfile(bfileRootDir, ...
                                  'update/pd/toolbox/compiler')); 
        file_list4 = prune_it(file_list4, prune_list);

	% Concatenate the file lists together
        % column vector addition	
        FileArray = [file_list1; file_list2; ...
                     file_list3; file_list4 ; additional_file_list];
        FileArray = prune_it(FileArray,prune_list_unix);
end

try
  [runtimeFiles FileArray] = get_runtime_files(FileArray, Arch);
  tbx_files = get_tbx_files(Arch);
  ctfzip(mcr_zipfile, FileArray, RootDir, tbx_files, runtimeFiles);
  FileArray = [FileArray ; { tbx_files.src }' ];
  if nargout == 2
    mcrlist = FileArray;
  end
catch
  err = lasterror;
  disp(sprintf('Building ZIP file failed.\n??? %s',err.message));
end

%==========================================================================
function fileList = match_files(filepatterns)
fileList = {};
for i=1:length(filepatterns)
    fileList = [ fileList ; ...
                 find_files(filepatterns(i).root, filepatterns(i).file) ];
end

%==========================================================================
function fileList = find_files(root, pattern)
% FIND_FILES Find all files matching a given pattern below a given root

fileList = {};

if (exist(root, 'file') == 0)
    error(['Directory ' root ' does not exist.']);
end

d = dir(root);
for i=1:length(d)
    % Skip over . and ..
    if (strcmp(d(i).name, '..') == 1 || strcmp(d(i).name, '.') == 1)
	continue;
    end

    if (d(i).isdir == 1)
        newdir = fullfile(root, d(i).name); 
        fileList = [ fileList ; find_files(newdir, pattern) ];
    elseif (~isempty(regexp(d(i).name, pattern, 'once')))
	filename = { fullfile(root, d(i).name) };
        fileList = [ fileList ; filename ];
    end
end

%==========================================================================
function [runtimeFiles, fileList] = get_runtime_files(fileList, Arch)
% GET_RUNTIME_FILES Move files from the bin into the runtime directory.
%  The runtime files are removed from the list, and returned in a 
%  separate list.
%
%  The version strings are different on the PC and UNIX. For the PC we're
%  looking for <anything><major><minor>.dll, while on UNIX we're looking for
%  <anything>.so.<major>.<minor>, where <major> and <minor> are the MCR's
%  major and minor revision numbers.

global mcrversion;

    removeList = [];
    removeRuntimeList = [];
    runtimeFiles = struct([]);

    disp('...Moving files from bin/<ARCH> to runtime/<ARCH>.');

if ispc
    % Set up the regular expression for the PC
    search_exp = ['(.*' mcrversion.pc '\.dll)'];
elseif (strcmp(Arch, 'mac'))  % Arch == lower(computer)
    % On the Mac, because we have no support for dynamic loading, we
    % move every file into the runtime/mac directory.
    search_exp = '.*';
elseif (strcmp(Arch, 'hpux'))
    % Silly HPUX uses .sl for shared libraries, rather than the nearly
    % universal .so.
    search_exp = ['(.*\.sl\.' mcrversion.unix ')'];
else
    % Set up the regular expression for non-Mac UNIX platforms
    search_exp = ['(.*\.so\.' mcrversion.unix ')'];
end

% Add architecture specific patterns to the search expression
if (isfield(mcrversion.patterns, Arch) && ...
    ~isempty(mcrversion.patterns.(Arch)))

    for i=1:length(mcrversion.patterns.(Arch))
        search_exp = strcat(search_exp, '|(', ...
                            mcrversion.patterns.(Arch){i}, ')' );
    end

end

% Loop over the list of input files. For each match, remember the
% index and save the name of the file in the runtimeFiles list.
vfilecount = 0;
for i=1:length(fileList)
    if (regexp(fileList{i}, search_exp))

	vfilecount = vfilecount + 1;

	% Remember the index of this file in the file list, for later
	% removal.
        removeList(end+1) = i;

	% Get the parts of the file name.
        [path,base,ext] = fileparts(fileList{i}); %#ok path not used
	basename = [base ext];

	% Create the output structure. src is the full path to the file,
	% dest is the relative path for the file in the ZIP archive.

        runtimeFiles(end+1).src = fullfile(matlabroot, fileList{i});
        runtimeFiles(end).dest = ...
              fullfile(mcrversion.string, 'runtime', ...
                       Arch, basename);
    end
end

files = 'files';
if (vfilecount == 1)
    files = 'file';
end;

disp(['...Moved ' num2str(vfilecount) ' ' files '.']);

if (length(runtimeFiles) > 0)

    % Find any links that point to files which have been moved.
    % Create new links that point to the new locations. First,
    % write the list of files to a temporary file.
    originalList = tempname;
    fid = fopen(originalList, 'w');
    if (fid == 0)
        error(['Cannot open original file list file ' originalList]);
    end
    for i=1:length(fileList)
        if (fprintf(fid, '%s\n', fullfile(matlabroot, fileList{i})) <= 0)
            fclose(fid);
            error(['Could not write ' fileList{i} ', file number ' ...
                  num2str(i) ' to original file list file ' originalList]);
        end
    end

    fclose(fid);

    % Prepare a list of files that were moved. <original path>:<new path>
    movedFiles=tempname;

    fid = fopen(movedFiles, 'w');
    if (fid == 0)
        error(['Cannot open moved file list file ' movedFiles]);
    end
    for i=1:length(runtimeFiles)
        if (fprintf(fid, '%s:%s\n', runtimeFiles(i).src, ...
                    runtimeFiles(i).dest) <= 0)
            fclose(fid);
            error(['Could not write ' runtimeFiles(i).src ', file number ' ...
                  num2str(i) ' to moved file list file ' movedFiles]);
        end
    end

    fclose(fid);

    % Determine which links must be replaced
    pscript = fullfile(matlabroot, 'toolbox', 'compiler', 'resetlinks.pl');
    [links, status] = perl(pscript, '-o', originalList, '-m', movedFiles, ...
                           '-r', matlabroot, '-n', mcrversion.string);

    % Delete the temporary files
    %delete(originalList);
    %delete(movedFiles);

    % links is a colon-separated list of transformations to perform
    %    old_link_file:new_link_file
    % For each line, old_link_file must be removed from the fileList,
    % and a new entry added to runtimeFiles.

    if (status ~= 0)
        error(links);
    end

    if (status == 0 && ~isempty(links))
	sfilecount = 0;

	[oldLinks, newLinks] = strread(links, '%s%s', 'delimiter', ':');

        % Remove the old links from the fileList by adding their indices to 
	% removeList.

        for i=1:length(oldLinks)
	    % Look and see if the "old link" is actually a "moved file."
	    deadFile = oldLinks{i};
	    for j=1:length(runtimeFiles)
                if (strcmp(fullfile(mcrversion.string, deadFile), ...
                           runtimeFiles(j).dest) == 1) 
                    deadFile = runtimeFiles(j).src;
	            deadFile = strip_matlab_root(deadFile);
	            % Both the link and the linked-to file are moving, so
	            % the entry in runtimeFiles for the link is redundant
	            % (we'll add one below).
		    removeRuntimeList = [removeRuntimeList j];
                end
	    end

	    indices = find(strcmp(deadFile, fileList) == 1);
	    if (~isempty(indices))
                removeList = [removeList indices];
	        sfilecount = sfilecount + length(indices);
	    end

	    % Add an entry for the new link.
            runtimeFiles(end+1).src = newLinks{i};
            runtimeFiles(end).dest = fullfile(mcrversion.string, oldLinks{i});
        end
	
	links = 'links';
        if (sfilecount == 1)
            links = 'link';
        end
        disp(['...Repointed ' num2str(sfilecount) ' symbolic ' links '.']);
    end
end

% Done searching and creating. Remove the runtime files from the file list.
if (~isempty(removeList))
    fileList(removeList) = [];
end
if (~isempty(removeRuntimeList))
    runtimeFiles(removeRuntimeList) = [];
end

%==========================================================================
function str = read_all(b_file)
% READ_ALL Read a list of file names from a given text file.

    % Always return a string type
    str = '';

    % Open the file
    fid = fopen(b_file);
    if (fid < 0)
        error(['Cannot open file ' b_file]);
    end

    try
        % Skip the first line
        str = fgetl(fid);

        % Read in all the text, as one giant string. Lines will be
        % separated by spaces.
        str = fread(fid,Inf,'char=>char');
        fclose(fid);
    catch
        disp(lasterr);
        fclose(fid);
    end

%==========================================================================
function files = get_tbx_files(Arch)

global mcrversion;

disp('...Getting list of files from toolbox/matlab.');

% Add the files from toolbox/matlab to the FileArray. The list of files to
% include is stored in toolbox/compiler/mcr/mcrlist.<arch>. The files are
% listed by relative path; convert the relative paths to full paths by adding
% MATLABROOT/toolbox/ to the front of each. This is a LONG list, 
% consisting of thousands of files.

% Read the architecture-specific (MEX files, mostly) files from the 
% architecture-specific file. It is a fatal error (game over, man, 
% game over!) if this file does not exist.
archfile=[matlabroot '/toolbox/compiler/mcr/mcrlist.' Arch];
if (exist(archfile,'file') == 0)
    error(['Architecture specific file list ''' ...
           archfile ''' does not exist.']);
end
archfiles = textread(archfile, '%s', 'delimiter', '\n', 'whitespace', '');

% Fullpath: prepend MATLABROOT/toolbox/matlab so that we grab these files
% from toolbox/matlab.
files=struct([]);
for i=1:numel(archfiles) 

    % Compute the full path to the source file
    fullsrcpath = [matlabroot '/toolbox/matlab/' archfiles{i}];

    % Don't add the file to the list if it doesn't exist.
    if (exist(fullsrcpath, 'file') == 0), continue, end;

    files(end+1).src = fullsrcpath;
    files(end).dest = fullfile(mcrversion.string, 'toolbox', 'matlab', ...
                               archfiles{i});
end

% Read the list of common files from the common file list file
commonfile=[matlabroot '/toolbox/compiler/mcr/mcrlist.common'];
if (exist(commonfile,'file') == 0)
    error(['Common file list ''' ...
           commonfile ''' does not exist.']);
end

tbxfiles = textread(commonfile, '%s', 'delimiter', '\n', 'whitespace', '');

% Fullpath: prepend MATLABROOT/toolbox/compiler/mcr/matlab so that we include
% the encrypted M-files.
for i=1:numel(tbxfiles)

    % Compute the full path to the source file
    fullsrcpath = [matlabroot '/toolbox/compiler/mcr/matlab/' tbxfiles{i}];

    % Don't add the file to the list if it doesn't exist.
    if (exist(fullsrcpath, 'file') == 0), continue, end;

    files(end+1).src = fullsrcpath;

    % Compute the relative path on the destination machine

    reldestpath = fullfile(mcrversion.string, 'toolbox', 'matlab', ...
                           tbxfiles{i});

    files(end).dest = reldestpath;

end

%==========================================================================
function file_list = prune_it(file_list,prune_regexp)

fprune_regexp = strcat(prune_regexp,'|'); 
fprune_regexp = [fprune_regexp{:}];
fprune_regexp = ['(' fprune_regexp(1:end-1) ')'];   
prune_list = regexp(file_list,fprune_regexp,'match');
prune_list = [prune_list{:}];
file_list = setdiff(file_list,prune_list);
file_list = reshape(file_list,length(file_list),1);

%==========================================================================
function pathlist = pc_matlab_pathlist(file)
%PC_MATLAB_PATHLIST	returns PATHLIST a cell array of paths
%   relative to MATLABROOT found in the productdata.dat file
%   on the PC only. The paths returned are associated only with
%   the MATLAB piece.
%
%   Location of file: $MATLAB/uninstall/productdata.dat
%
%   Algorithm:
%   1. Open file and read line by line until line starting with 'Files:'.
%   2. Find the line for the compiler to get the index.
%      Version:1.0
%      Products:
%      ...
%      33,MATLAB Compiler,4.0,Mon Sep 29 02:12:02 EDT 2003,DEMO,1
%      ...
%      The index is the first number on the line. The index is attached
%      to the individual files in the (long) list below -- we want only
%      those files that belong to Compiler-based products.
%   3. Find the line(s) for any and all Builders to get their indices.
%   4. Read the rest of the file into memory as a string.
%   5. Use strread to read the file into path and index lists.
%   6. Return the paths with index set to 1.
%
%   Files:
%   toolbox\wavelet\wavelet\ja\meyer.m,65
%   ...
%---------------------------------------------------------------------


  fid = fopen(file);
  if fid < 0
    error (['Cannot open file: ''' file '''']);
  end
  n = 0;
  found_compiler = false;
  compiler_number = [];
  while 1
    tline = fgetl(fid);
    if (strncmp('Files:',tline,6) ~= 0)
      break
    end
    n = n + 1;
    if n > 2 && ~found_compiler
      [number,name] = strread(tline,'%d%s%*[^\n]','delimiter',',');
      builder_prefix = 'MATLAB Builder for';
      if (strcmp(name,'MATLAB Compiler') || ...
          strncmp(name, builder_prefix, numel(builder_prefix)))
        compiler_number(end+1) = number;
        found_compiler = true;
      end
    end
  end
  str = fread(fid,Inf,'char=>char');
  fclose(fid);

  % The index is the number after the first comma
  [pathlist,index] = strread(str,'%s%d','delimiter',',');

  if found_compiler
    base_files = pathlist(find(index == 1));
    compiler_files = pathlist(find(ismember(index, compiler_number)));
    pathlist = [ base_files ; compiler_files ];
  else
    pathlist = pathlist(find(index == 1));
  end
  return
%==========================================================================
function file_list = read_b_file(dir) 
%READ_B_FILE	opens a .b file on UNIX and reads the file list that
%   starts on line 2 of the file. It returns the file list as a cell
%   array in FILE_LIST.

% First look for a .b file; if that can't be found, look for a b file
% (BaT mandates this behavior.)

file = fullfile(dir, '.b');

fid = fopen(file);

if fid < 0
    file = fullfile(dir, 'b');
    fid = fopen(file);
end

if fid < 0
    error (['Cannot open b-file in directory: "' dir '"']);
end

str = fread(fid,Inf,'char=>char');
fclose(fid);
[file_list] = strread(str,'%s%*[^\n]','headerlines',1);

%===============================================================================
function status = isAbsolute(file)
if ispc
    status = ~isempty(regexp(file,'^[a-zA-Z]*:\\','once')) || ...
                      strncmp(file,'\\',2);
else
    status = strncmp(file,'/',1);
end

%===============================================================================
function state = exist2(file,type)
%EXIST2 is a workaround for EXIST for R14beta2.
%   There is a major bug (184108) in which exist does not work
%   with files that start with '.' and include a partial path.
%   If you are in the directory where the '.' file exists and give the
%   simple filename it works.

  state = exist(file,type);

%state = 0;
%[d,f,e] = fileparts(file);
%if isempty(f)

  % Starts with a . Move to that directory and run the command
  % Remove when gecko 184108 is fixed.
  %
%  oldpwd = pwd;
%  cd(d);
%  state = exist(e,type);
%  cd(oldpwd);
%else
%  state = exist(file,type);
%end

%===============================================================================
function pathlist = prune_duplicates(pathlist,rootdir)
%PRUNE_DPLICATES is a workaround for problems with the .b files in R14beta2.
%   The .b files contain directories names as well as some files in those
%   directories resulting in duplicates being found by zip().

% Remove the obvious directories
%
pathlist = sort(pathlist);
for i=2:length(pathlist)
  match = [pathlist{i-1} filesep];
  if strncmp(match,pathlist{i},length(match))
     pathlist{i-1} = '';
  end
end
pathlist = pathlist(~strcmp(pathlist,''));

% Now go through and handle the rest I can't figure out.
% use the code from zip. 

% Run through the initial pathlist
%
extra = {};
for i=1:length(pathlist)
  if exist2(fullfile(rootdir,pathlist{i}),'dir')
    dirContents = dir(fullfile(rootdir,pathlist{i}));
    listing = {dirContents.name};
    listing = setdiff(listing,{'.','..'});
    if length(listing) > 0
      for j = 1:length(listing)
        extra{end+1,1} = fullfile(pathlist{i},listing{j});
      end
      pathlist{i} = '';
    end
  end
end
pathlist = pathlist(~strcmp(pathlist,''));

% Recursively run through the extra list
%
n = 0;
while length(extra) > n
  n = n + 1;
  if exist2(fullfile(rootdir,extra{n}),'dir')
    dirContents = dir(fullfile(rootdir,extra{n}));
    listing = {dirContents.name};
    listing = setdiff(listing,{'.','..'});
    if length(listing) > 0
      for i = 1:length(listing)
        extra{end+1,1} = fullfile(extra{n},listing{i});
      end
      extra{n} = '';
    end
  end
end
extra = extra(~strcmp(extra,''));

%================================
% fprintf('length(extra) = %d\n',length(extra));
%================================
pathlist = unique([pathlist; extra]);

%==========================================================================
function directory = expand_dir_name(Arch, dirname)
% EXPAND_DIR_NAME Process a directory name like the shell would
%
%   Expand any shell metacharacters like ~, and interpolate the values of
% any environment variables.

directory = '';

if (length(dirname) > 0)

    % Use the system's ECHO command to interpret any metacharacters in the
    % directory name. This is extremely shell-dependent, and will not work
    % in shells where ECHO does not exist or which do not support 
    % metacharacters.

    [status, result] = system(['echo ' dirname]);
    if (status == 0)
        dirname = strtrim(result);
    end;

    % Now look for (and interpolate) any environment variables in the
    % directory name.

    % Set up regular expressions to match environment variable syntax on
    % the supported platforms.
    if (strcmp(Arch, 'win32'))
        pattern = '%(?<name>[^%])%';
    else
        pattern = '\$(?<name>[A-Za-z0-9_]+)';
    end

    % Search the directory name string for variables
    matches = regexp(dirname, pattern, 'names');

    % If we found any matches, use GETENV to fetch the values and
    % STRREP to interpolate that value back into the string.
    for i=1:length(matches)
        dirname = strrep(dirname, env_syntax(Arch, matches(i).name), ...
                         getenv(matches(i).name));
    end

    directory = dirname;

end

function name = env_syntax(Arch, vname)
% ENV_SYNTAX Given a string, make an environment variable reference of it
%
% PC  : string -> %string%
% UNIX: string -> $string
%
    if (strcmp(Arch, 'win32'))
        name = ['%' vname '%'];
    else
        name = ['$' vname];
    end


%==========================================================================
function mcr_zipfile = get_mcr_zipfile(Arch,mcr_zipfile_dirname, ...
			               mcr_zipfile_filename)
%GET_MCR_ZIPFILE	determines the location where to put the ZIP file.
%   fore achitecture ARCH.
%
%   If MCR_ZIPFILE_DIRNAME is empty then use:
%
%   <matlabroot>/toolbox/compiler/deploy/<arch>/MCRInstaller.zip 
%
%   If MCR_ZIPFILE_FILENAME is empty then use:
%
%   <MCR_ZIPFILE_DIRNAME>/MCRInstaller.zip 
%
%   If neither are empty then use:
%
%   <MCR_ZIPFILE_DIRNAME>/<MCR_ZIPFILE_FILENAME>
%
%   The directory is created as needed.

mcr_zipfile = '';
%
if isempty(mcr_zipfile_dirname)
  mcr_zipfile_dirname = fullfile(matlabroot,'toolbox', ...
				 'compiler','deploy',Arch);
end
if ~exist(mcr_zipfile_dirname, 'dir')
  [status,msg] = mkdir(mcr_zipfile_dirname);
  if ~status
    disp(sprintf('Directory "%s" for ZIP file could not be built.\n??? %s', ...
                 mcr_zipfile_dirname, msg));
    return
  end
end
if isempty(mcr_zipfile_filename)
  mcr_zipfile_filename='MCRInstaller.zip';
end
mcr_zipfile = fullfile(mcr_zipfile_dirname,mcr_zipfile_filename);

%==========================================================================

function [entries, found] = addentry(entries, path, file, entryname)
% Add a file entry to the list of file entries. Modifies the input
% list and returns it.

    global mcrversion;

    filePath = fullfile(path, file);
    if ispc
        filePath = strrep(filePath,'/','\');
    end
    fileType = exist2(filePath,'file');
    if (fileType == 0)
	warning('COMPILER:BUILDMCR:NoSuchFile',...
                '"%s" does not exist.', filePath);
	found = false;
    elseif (fileType < 2 || fileType > 6)
	warning('COMPILER:BUILDMCR:WrongFileType',...
                '"%s" cannot be put into a ZIP file.', filePath);
	found = false;
    else
        entries(end+1).file = filePath;
        % Allow absolute paths, but issue a warning. TODO: Perhaps strip
	% drive letter on the PC?
        if (isAbsolute(entryname))
	    warning('COMPILER:BUILDMCR:AbsolutePathEntry',...
                '"%s" added as absolute path.', entryname);
	    entries(end).entry = entryname;
        else
            entries(end).entry = fullfile(mcrversion.string, entryname);
        end
	found = true;
    end

%==========================================================================

function [entries, notfound] = adddirectory(entries, filePath, filename)
% Add all the files in the directory, and all those in all the subdirectories
% to the file list.

notfound = 0;
dirContents = dir(filePath);
listing = {dirContents.name};

% Remove . and .. from the list 
listing = setdiff(listing,{'.','..'});

% Add directory entries to the list of inputs
for j = 1:length(listing)

    fullPath = fullfile(filePath, listing{j});

    % If the directory entry is a subdirectory, add it to the
    % list of files to be processed. Files is the variable that
    % controls the execution of this loop.

    if (exist2(fullPath, 'dir'))
       entries = adddirectory(entries, fullPath, ...
                              fullfile(filename, listing{j}));

    % Plain file, add to entry list
    else 
        % The entry name should be relative to the original
        % directory as specified in the file list.
	[entries, found] = addentry(entries, filePath, listing{j}, ...
                                    fullfile(filename, listing{j}));
        if (found == false)
	    notfound = notfound + 1;
        end
    end
end

%==========================================================================
function stripped_file = strip_matlab_root(filename)
% Strip the MATLAB root from a file name (if present)

stripped_file = filename;
if (strncmp(matlabroot, filename, length(matlabroot)) == 1)
    % Skip the entire MATLABROOT and the PATHSEP that terminates it
    stripped_file = filename(length(matlabroot)+2:end);
end

%==========================================================================

function ctfzip(zipFilename, files, rootDir, aliases, runtimeFiles)
% Use CTFARCHIVER to create a ZIP file

% Work around file problems in the UNIX file lists.
if isunix
    files = prune_duplicates(files,rootDir);
end

% Parse arguments.
error(nargchk(2,5,nargin))
if (nargin < 3)
    rootDir = pwd;
end

if ischar(files)
    files = {files};
end

% If no extension is given for output, add ".zip".
[null,null,zipFilenameExt]=fileparts(zipFilename); %#ok  null is a dummy
if isempty(zipFilenameExt)
    zipFilename = [zipFilename '.zip'];
end

% Create a structure of the inputs.
entries = {};
missing = 0;
for i = 1:length(files)
    filename = files{i};
    if isAbsolute(filename)
        [path,base,ext] = fileparts(filename);
        file = [base ext];
	% If the filename begins with the MATLAB root, remove the root from
	% the name.
	filename = strip_matlab_root(filename);
    else
        path = rootDir;        
        file = filename;
    end

    filePath = fullfile(path, file);

    % If the filename is a directory, add all the files in the directory
    % to the list.
    if exist2(filePath,'dir')
        [entries, notfound] = adddirectory(entries, filePath, filename);
	missing = missing + notfound;
    else
	[entries, found] = addentry(entries, path, file, filename);
        if (found == false)
	    missing = missing + 1;
        end
    end

end

% If there is nothing to do, error.  There is no such thing as an "empty zip".
if isempty(entries)
    error('Nothing to zip.')
end

if (missing ~= 0)
    warning('COMPILER:BUILDMCR:MissingFiles',...
            ['Attempted to add ' num2str(missing) ' files that were not found.']);
end

disp(['...Target ZIP file: ' zipFilename]);
disp(['...Adding platform-specific binaries (' ...
      num2str(numel(entries)) ' files) and toolbox/matlab']);
disp(['...content (' num2str(numel(aliases)) ...
      ' files). Please wait. This process cannot be interrupted.']);


srcFiles = [ {entries.file} {aliases.src} ];
zipEntries = [ {entries.entry} {aliases.dest} ];

% If the list of runtime files is not empty, add the runtime files
% to the archive.
if (~isempty(runtimeFiles))
    srcFiles = [ srcFiles {runtimeFiles.src} ];
    zipEntries = [ zipEntries {runtimeFiles.dest} ];
end

% Each file in the zip archive must have a unique destination name. Check
% for multiple files with the same destination name. (The destination name
% is the full path to the file when extracted from the ZIP archive.)
%
% If the list of unique destination names is shorter than the list of 
% destination names, there are at least two files with the same destination
% name.

allNames = zipEntries;
[uniqueNames,uniqueIdxs] = unique(allNames);
if length(uniqueNames) < length(zipEntries)

    % For those files with non-unique destination names, compare the
    % time stamps of the source files and include only the most recent
    % version of the file in the archive. Issue a warning for each such
    % file.

    duplicates = setdiff(1:length(zipEntries), uniqueIdxs);

    deleteMe = [];
    for i=duplicates

        % Find all the source files with this destination.

	dupIndices = find(strcmp(zipEntries, zipEntries{i}));

        % Eliminate all but the one with the most recent modification time.

	timestamp = [];
	for j=dupIndices
	    info = dir(srcFiles{j});
	    if (isempty(info))
                warning('COMPILER:BUILDMCR:NonexistentFile', ...
                        ['Cannot determine modification date for ' ...
                         srcFiles{j} ' because it does not exist.']);
                continue;
            end
	    timestamp(end+1) = datenum(info.date);
        end
	[m, maxIdx] = max(timestamp);
	deleteMe = ...
             [deleteMe dupIndices(setdiff(1:length(dupIndices), maxIdx))];

	% Let them know there's something rotten in the state of Denmark

	src = setdiff(dupIndices, deleteMe);
	wmsg = sprintf('More than one file with destination "%s".\nUsing latest source file: "%s"',...
                       zipEntries{i}, srcFiles{src});
        warning('COMPILER:BUILDMCR:DuplicateDestination', wmsg);

    end

    % Delete the older source files from the list.
    srcFiles(deleteMe) = [];
    zipEntries(deleteMe) = [];

end

% Create the ZIP file by invoking a MEX-function that calls into the 
% CTFARCHIVER library. We need to use CTFARCHIVER's ZIP function rather
% than MATLAB's because CTFARCHIVER's ZIP will allow us to rename files
% (i.e., place a file into the CTF archive with a different name (or path)
% than it has in the source file hierarchy.
try
    ctfcreatezip(zipFilename, srcFiles, zipEntries);
catch
    err = lasterror;
    str = sprintf('CtfCreateZip failed:\n%s', err.message);
    disp(str);
end

