function bundle(dst, src, MAX_CHUNK)

% BUNDLE Bundler for a collection of M-files.
%  BUNDLE('DST') creates the bundle file 'DST'.
%  BUNDLE('DST','SRC') opens the bundle file 'DST'
%   and adds the 'SRC' M-file routine to it.  The
%   bundled routines can then be installed at another
%   site by executing the 'DST' M-file in MATLAB 4.0+.
%   'DST' and 'SRC' may include the explicit path,
%   but do not include ".m" in 'DST'.
%   If 'SRC' is a string matrix, each of the
%   named routines is added to the 'DST' bundle.
%  BUNDLE('DST','SRC',MAX_CHUNK) sets the maximum size
%   of a bundle chunk to MAX_CHUNK lines, which
%   defaults to 32.
%  BUNDLE (no arguments) demonstrates itself.
 
% Copyright (C) 1994 Dr. Charles R. Denham, ZYDECO.
% All Rights Reserved.

% Original Version 15-Jul-94 at 12:00:05.217.
% Revised  Version  2-Aug-94 at 16:12:26.32.   Chunkified.

if nargin < 2, src = ''; end    % No source file.
if nargin < 3, MAX_CHUNK = 32; end

if nargin < 1   % Demonstration.
   help bundle
   disp('Original:')
   hello, world
   disp(' ')
   disp('Bundling:')
   dst = 'mybundle';
   src = ['hello'; 'world'];
   bundle(dst)
   bundle(dst, 'hello')
   bundle(dst, 'world')
   disp(' ')
   disp('Unbundling:')
   eval('mybundle')
   disp(' ')
   disp('Recreated:')
   hello, world
   return
end

% Date/time.

c = clock;
time = '';
for i = 1:6
   t = int2str(c(i));
   while length(t) < 2, t = ['0' t]; end
   if i < 6, t = [t '.']; end
   time = [time t];
end

% Get the destination function name.

dfunc = dst;
while any(dfunc == ':'), dfunc(1) = ''; end   % Macintosh.
while any(dfunc == '/'), dfunc(1) = ''; end   % Unix.
while any(dfunc == '\'), dfunc(1) = ''; end   % PC.
while any(dfunc == '.'), dfunc(length(dfunc)) = ''; end

% Set up macros.

NL = sprintf('\n');
NLS = [''' NL ...' NL];
newline = ['NL = sprintf(''\\n'');'];

NEW_FILE = ['NEW_FILE = ''fp = fopen([mfunc ''''.m''''], ''''w'''');'';']; % Create a new file.
ADD_FILE = ['ADD_FILE = ''fprintf(fp, mtext);'';']; % Append to the file.
END_FILE = ['END_FILE = ''fclose(fp);'';']; % Close the file.

NEW_CHUNK = ['mtext = [ ...' NL];
END_CHUNK = ['];' NL];

NEW_LINE = [''''];             % Initialize a new line.
END_LINE = [''' NL ...' NL];   % Terminate the current line.

PROGRESS= ['PROGRESS = ''disp([''''    unbundled: '''' mfunc])'';'];

special = [NL(1) '%' '\' ''''];

preamble = [
 NL ...
'%% Generated by BUNDLE: ' dst NL ...
'%% Created: ' time NL ...
'%% Copyright (C) 1994 Dr. Charles R. Denham, ZYDECO.' NL ...
'%% All Rights Reserved.' NL ...
'%% E-Mail: cdenham@nobska.er.usgs.gov.' NL ...
'%% WWW: http://priapus.er.usgs.gov.' NL ...
NL ...
% 'help ' dfunc NL ...
% NL ...
];

% Initialize the new dst file if needed.

if nargin < 2 | exist([dst '.m']) ~= 2
   fp = fopen([dst '.m'], 'w');
   fprintf(fp, ['function ' dfunc NL]);
   fprintf(fp, preamble);
   fprintf(fp, [newline NL]);
   fprintf(fp, [NEW_FILE NL]);
   fprintf(fp, [ADD_FILE NL]);
   fprintf(fp, [END_FILE NL]);
   fprintf(fp, [PROGRESS NL]);
   fclose(fp);
end

[m, n] = size(src);

for k = 1:m   % Loop over all source files.

% Input the source as a text stream.

s = src(k, :);

sfunc = s;
while any(sfunc == ':'), sfunc(1) = ''; end   % Macintosh.
while any(sfunc == '/'), sfunc(1) = ''; end   % Unix.
while any(sfunc == '\'), sfunc(1) = ''; end   % PC.
while any(sfunc == '.'), sfunc(length(sfunc)) = ''; end

s = s(s ~= ' ' & s ~= setstr(0));
if exist([s '.m']) == 2
   fp = fopen([s '.m'], 'r');   % M-file takes precedence.
  elseif exist(s) == 2
   fp = fopen(s, 'r');
end
mtext = setstr(fread(fp, inf, 'char').');
fclose(fp);

% Adjust for various newline schemes.

if length(NL) == 2
   mtext(mtext == NL(2)) = [];
end

% Output buffer: enough to hold a line.

f = [0 find(mtext == NL(1)) length(mtext)+1];
buflen = 2 .* max(diff(f));
buffer = setstr(zeros(1, buflen) + abs(' '));

% Package and output to the destination file.

fp = fopen([dst '.m'], 'at');
fprintf(fp, NL);
NEW_FUNC = ['mfunc = ''' sfunc ''';' NL];
fprintf(fp, NEW_FUNC);
fprintf(fp, ['eval(NEW_FILE);' NL]);
begin_chunk = 1;   % Needs mtext = [ ... .
buflen = 0;   % Clear the buffer.
line_count = 0;
for i = 1:length(mtext)
   if line_count >= MAX_CHUNK
      fprintf(fp, END_CHUNK);
      fprintf(fp, ['eval(ADD_FILE);' NL]);
      begin_chunk = 1;
   end
   if begin_chunk
      fprintf(fp, NEW_CHUNK);
      begin_chunk = 0;
      begin_line = 1;    % Needs an opening quote.
      line_count = 0;
   end
   if begin_line
      buflen = 0;
      buffer(buflen + 1:length(NEW_LINE)) = NEW_LINE;
      buflen = buflen + length(NEW_LINE);
      begin_line = 0;
   end
   c = mtext(i);
   if any(c == special)
      if c == NL(1)
         buffer(buflen + (1:length(END_LINE))) = END_LINE;
         buflen = buflen + length(END_LINE);
         fprintf(fp, buffer(1:buflen));
         buflen = 0;
         line_count = line_count + 1;
         begin_line = 1;
        elseif c == '%'
         buffer(buflen + (1:4)) = '%%%%';
         buflen = buflen + 4;
        elseif c == ''''
         buffer(buflen + (1:2)) = '''''';
         buflen = buflen + 2;
        elseif c == '\'
         buffer(buflen + (1:4)) = '\\\\';
         buflen = buflen + 4;
      end
     else
      buffer(buflen + 1) = c;
      buflen = buflen + 1;
   end
end

if buflen > 0
   fprintf(fp, buffer(1:buflen));
end

if begin_chunk == 0
   fprintf(fp, END_CHUNK);
   fprintf(fp, ['eval(ADD_FILE);' NL]);
end

fprintf(fp, ['eval(END_FILE);' NL]);
fprintf(fp, ['eval(PROGRESS);' NL]);

disp(['    bundled in ' dst ': ' sfunc])

fclose(fp);

end
