function mtlb_save_dbg(M4_filename, varargin)
//save variable under  matlab 4.x .mat binary or ascii format files
//
M4_var_in = argn(2)
M4_flags = []
M4_vars = []
M4_RGS = []

M4_legal_flags = ['-ascii' '-double' '-tabs' '-little_endian' '-big_endian' '-append' '-write'];

M4_illegal_vars = [ '%toolboxes';'%scicos_menu';'%scicos_short';'%scicos_help';'%scicos_display_mode';
		'MSDOS';'home';'PWD';'TMPDIR';'%helps';'LANGUAGE';'scicos_pal';'WSCI';
		'SCI';'%F';'%T';'%z';'%s';'%nan';'%inf';'COMPILER';'%gtk';'%pvm';'%tk';'$';'%t';'%pi';
		'%f';'%eps';'%io';'%i';'%e';'demolist';'%browsehelp';'%toolboxes_dir';'varargin'];

function found = boolgrep(S1, S2);
	found = [];
	if (grep(S1, S2) == []) then found = %F; else found = %T; end
endfunction

function alt_byte(X)
// alternative storage function
//standard save - no byte reduction - 64b
fmt = 'd';
MOPT(3) = 0;

if (and(int(real(X))==real(X)))&(and(int(imag(X))==imag(X))) then 
	X_max = max([max(real(X)) max(imag(X))]);
	X_min = min([min(real(X)) min(imag(X))]);

	if (X_max <= 255)&(X_min >= 0)
		//disp('>10k save, unsigned char')
		fmt = 'uc';
		MOPT(3) = 5;
	elseif (X_max <= 65535)&(X_min >= 0)
		//disp('>10k save, unsigned short')
		fmt = 'us';
		MOPT(3) = 4;
	elseif (X_max <= 32767)&(X_min >= -32768)
		//disp('>10k save, signed short')
		fmt = 's'; // or 'i'
		MOPT(3) = 3;
	elseif (X_max <= 2147483647)&(X_min >= -2147483648)
		//disp('>10k save, signed long')
		fmt = 'l';
		MOPT(3) = 2;
	end
end
endfunction

// get convenient string arrays of arguments
// note that this only works using the unusual macro call
for M4_1 = 1:M4_var_in - 1 do	M4_RGS = [M4_RGS varargin(M4_1)];end

M4_flags = convstr(M4_RGS(grep(convstr(M4_RGS), M4_legal_flags)));

if size(grep(M4_RGS,'-'),'*') > size(M4_flags,'*') then
	M4_flag_exceptions = M4_RGS(grep(M4_RGS, '-'));
	M4_flag_exceptions = M4_flag_exceptions(convstr(M4_flag_exceptions)~=(M4_flags));
	errstr = ' ignoring unknown flag <<'+M4_flag_exceptions+'>> in mtlb_save_dbg';
	warning(errstr);
	M4_RGS(grep(M4_RGS, M4_flag_exceptions)) = [];
end

if size(M4_RGS,'*') > size(M4_flags,'*') then
	M4_vars = M4_RGS; 
	M4_vars(grep(convstr(M4_RGS),M4_flags)) = []; //> 1 flag
	if grep(M4_vars,['M4_','varargin','mtlb_'])
		error('Name conflict with mtlb_save variables: '+ ...
			M4_vars(grep(M4_vars,['M4_','varargin','mtlb_'])));
	end
else
	M4_vars = who('get');
  M4_vars(grep(M4_vars,M4_illegal_vars)) = [];
	M4_vars(grep(M4_vars,'M4_')) = [];

	for M4_0 = 1:size(M4_vars,'*')
   		M4_valid_vars(M4_0) = or(eval('type('+M4_vars(M4_0)+')')==[1 4 5 8 10]);
	end
	M4_vars = M4_vars(M4_valid_vars);
	if M4_vars==[] then return, end;
end;

//disp(M4_vars,'varargs',M4_filename,'filename')

if (length(M4_filename) > 12)
	warning(' '+M4_filename+' :16-bit programs have shorter file names with no whitespace characters');
end

if boolgrep(M4_flags, '-append') then 
	M4_append_or_write = 'a';
else
	M4_append_or_write = 'w';
end

if ~boolgrep(M4_flags, '-ascii') then //binary save

	// extension check: note only binary file load assumes .mat extension
	if ~boolgrep(M4_filename, '.') then M4_filename = M4_filename+'.mat'; end

	// M=0 default with -LITTLE_ENDIAN,-BIG_ENDIAN flags
	MOPT = [bool2s(boolgrep(M4_flags, '-big_endian')) 0 0 0];

	[M4_fid,err]=mopen(M4_filename,M4_append_or_write+'b',MOPT(1));

	if err then
		error(M4_filename+' could not be created for writing');
	end

	for M4_0 = 1:size(M4_vars,'*')
		execstr('X='+M4_vars(M4_0));
	
		if ((type(X)==4)|(type(X)==8)|(type(X)==10)) then
			imagf = 0;
		else
			imagf = bool2s(or(imag(X(:))~=0));
		end
		
		[mrows,ncols] = size(X);
		
		namelen = length(M4_vars(M4_0)) + 1;
		
		name = [ascii(M4_vars(M4_0)) 0]; //NULL TERMINATION 
				
		select type(X)

		case 1  					//constant
			MOPT(4) = 0;
			if (mrows*ncols > 10000) then 
				alt_byte(X);// alternative storage
			else					 //floating point elements, standard storage
				fmt = 'd';
				MOPT(3) = 0;
			end
		
		case 4						//boolean matrix
		
			MOPT(4) = 2;
			X = sparse(bool2s(X')); //Matlab generates row/column indices in different order see below
			[sp_ji, sp_v, sp_nm]=spget(X);
			X = [sp_ji(:,2) sp_ji(:,1) sp_v; sp_nm 0];
			[mrows,ncols] = size(X);

			if (mrows*ncols > 10000) then 
				alt_byte(X);// alternative storage
			else					 //floating point elements, standard storage
				fmt = 'd';
				MOPT(3) = 0;
			end

		case 5		 				//sparse matrix
		
			MOPT(4) = 2;
			// When Matlab 4.2 converts a matrix to sparse format
			// it generates i,j indices by moving down the columns of the matrix
			// when Scilab-2.7.2 does the equivalent, it lists the non-zero entries
			// i&j component as it moves across the rows of the matrix
			// Matlab also enters the size of the full matrix as an integer 
			// at the end of the i-index vector (full rows) and an equivalent at the end
			// of the j-index vector (full cols); to return data in m*n form it pads 
			// the values vector with a NULL
			// Note mrows,ncols in header refer to size of saved 
			X = sparse(full(X).'); 
			[sp_ji,sp_v,sp_nm]=spget(X);
		
			if (imagf == 0) then
				X = [sp_ji(:,2) sp_ji(:,1) sp_v; sp_nm 0];
			else
				X = [sp_ji(:,2) sp_ji(:,1) real(sp_v) imag(sp_v); sp_nm 0 0];
			end
			[mrows,ncols] = size(X);

			if (mrows*ncols > 10000) then 
				alt_byte(X);// alternative storage
			else					 //floating point elements, standard storage
				fmt = 'd';
				MOPT(3) = 0;
			end

	
		case 8 						// 1,2,4 byte integer matrix,
			//Note Scilab does not support an imaginary component within the 
			//same matrix when defined in this format
			MOPT(4) = 0;

			if (mrows*ncols > 10000) then 			// alternative storage
	
				M4_int = inttype(X);
			
				select M4_int
				case 11 
					fmt = 'uc';
					MOPT(3) = 5;

				case 1
					X = uint16(X);
					fmt = 'us';
					MOPT(3) = 4;
			
				case 12
					fmt = 'us';
					MOPT(3) = 4;
			
				case 2
					fmt = 's';
					MOPT(3) = 3;
			
				case 4
					fmt = 'l';
					MOPT(3) = 2;
			
				case 14
					X = double(X);
					fmt = 'd';
					MOPT(3) = 0;
			end
			else					//standard storage
				X = double(X);
				fmt = 'd';
				MOPT(3) = 0;
			end

		case 10 					//string matrix

			// Matlab 4.2c.1 does not save strings in the format given
			// It does however read them...
			// It seems to store short strings in 64-bit double format per character
		
			MOPT(4) = 1;

			// pad max(length(X)) convert matrix of strings to one long string?
			// pads string arrays to form mrows*ncols and stores columnwise
			if (mrows*ncols > 1) then
				for c = 1:ncols
					longest_str = max(length(X(:,c)));
					for r = 1:mrows
						if longest_str - length(X(r,c)) > 0 then
							blanks = code2str(40 .*ones(1,(longest_str - length(X(r,c)))));
							X(r,c) = X(r,c) + blanks;
						end
					end
				end
			end
				
			// Matlab 4 considers each character as a matrix entry
			// concatenate columns togther and reshape strings columnwise
			// modify mrows & ncols accordingly
		
			if (ncols > 1) then
				for c = 2:ncols
					X(:,c) = X(:,c - 1) + X(:,c);
				end
				X = X(:,ncols);
				ncols = length(X(1,1));
			end

			X = ascii(X);
		
			if (mrows > 1) then
			  X = matrix(matrix(X,ncols,mrows)',1,-1) //convert to columnwise vector
			end

			if (mrows*ncols > 10000) then
				MOPT(3) = 5;		
				fmt = 'uc';
			else	
				MOPT(3) = 0;
				fmt = 'd';
			end
		end // M4_type

		header = [MOPT*[1000;100;10;1] 
			  mrows
			  ncols
			  imagf
			  namelen]';
		
		if (imagf == 1) then
			X = [real(X) imag(X)];
		end
	
		if (MOPT(1) == 1)
			machine = 'b'; //big-endian
		else
			machine = 'l';
		end
		
		mput(header,'ui'+machine,M4_fid);
		mput(name,'c'+machine, M4_fid);

		mput(X, fmt+machine, M4_fid);
	
	end // all M4_vars
	err = mclose(M4_fid);
	if err then error(' failed to close: '+ M4_filename); end

end // binary save



if boolgrep(M4_flags, '-ascii') then //ascii save


	if boolgrep(M4_flags,'-double') then //16 digits save
		M4_fmt = ' % #2.16e';
	else
		M4_fmt = ' % #2.7e';
	end

	if boolgrep(M4_flags, '-tabs') then M4_fmt = '\t'+M4_fmt; end

	[M4_fid,err]=mopen(M4_filename,M4_append_or_write+'b',bool2s(boolgrep(M4_flags, '-big_endian')));
	if err then
		error(M4_filename+" could not be created for writing");
	end

	// warn if saving multiple seperate strings or
	// saving strings alongside other types
	M4_string_anomaly = %F; 

	for M4_0 = 1:size(M4_vars,'*') do   // perform changes on variables

		execstr('X ='+M4_vars(M4_0));

		select type(X)
		case 1 					//constant
			// replicating a shortcoming in matlab 4 whereby only real elements are saved
			for M4_1 = 1:size(X,1) do
				mfprintf(M4_fid, M4_fmt, real(X(M4_1,:)'));
				mfprintf(M4_fid, '\n');
			end

		case 4						//boolean matrix

			for M4_1 = 1:size(X,1) do
				mfprintf(M4_fid, M4_fmt, bool2s(X(M4_1,:)'));
				mfprintf(M4_fid, '\n');
			end

		case 5		 				//sparse matrix
			// interestingly enough, matlab 4 actually represents the imaginary components here

			// When Matlab 4.2 converts a matrix to sparse format
			// it generates i,j indices by moving down the columns of the matrix
			// when Scilab-2.7.2 does the equivalent, it lists the non-zero entries
			// i&j component as it moves across the rows of the matrix
			// Matlab also enters the size of the full matrix as an integer 
			// at the end of the i-index vector (full rows) and an equivalent at the end
			// of the j-index vector (full cols);
			X = sparse(full(X).');// non-conjugate transpose
			[sp_ji,sp_v,sp_nm]=spget(X);

			if and(imag(sp)==0) then
				X = [sp_ji(:,2) sp_ji(:,1) sp_v];
				for M4_1 = 1:size(X,1) do	
					mfprintf(M4_fid, '%8u%8u'+M4_fmt+'\n', X(M4_1,:));
				end
				mfprintf(M4_fid, '%8u%8u'+M4_fmt+'\n', sp_nm(1,2), sp_nm(1,1), 0);
			else
				X = [sp_ji(:,2) sp_ji(:,1) real(sp_v) imag(sp_v)];
				for M4_1 = 1:size(X,1) do	
					mfprintf(M4_fid, '%8u%8u' + M4_fmt + M4_fmt + '\n', X(M4_1,:));
				end
				mfprintf(M4_fid, '%8u%8u' + M4_fmt + M4_fmt + '\n', sp_nm(1,2), sp_nm(1,1), 0, 0);
			end

		case 8 						// 1,2,4 byte integer matrix,

			for M4_1 = 1:size(X,1) do
				mfprintf(M4_fid, M4_fmt, double(X(M4_1,:))');
				mfprintf(M4_fid, '\n');
			end

		case 10 					//string matrix
			// Matlab 4 is particularly weak here
			// pad string arrays to form mrows*ncols and stores columnwise
			if (size(X,'r')*size(X,'c') > 1) then
				for c = 1:size(X,'c')
					longest_str = max(length(X(:,c)));
					for r = 1:size(X,'r')
						if longest_str - length(X(r,c)) > 0 then
							blanks = code2str(40 .*ones(1,(longest_str - length(X(r,c)))));
							X(r,c) = X(r,c) + blanks;
						end
					end
				end
			end
			if M4_string_anomaly then
				warning(" Matlab4.2 cannot read string matrices from ascii MAT-files when saved alongside other data types");	
			end
			M4_string_anomaly = %T;
			for M4_1 = 1:size(X,1) do
				mfprintf(M4_fid, M4_fmt, ascii(X(M4_1,:))');
				mfprintf(M4_fid, '\n');
			end
		end // select type
	M4_string_anomaly = %T;
	end // M4_vars(1..n)
	file('close',M4_fid)
end //ascii save

endfunction

// mput	P	Type		Length		Range
// 
// uc	5	unsigned char 	8 bits		0		  to	 255
// us	4	unsigned int	16 bits		0 		  to	 65,535 
// s	3	short int	16 bits		-32,768 	  to	 32,767 
// i	3	int		16 bits		-32,768 	  to	 32,767 
// l	2	long		32 bits		-2,147,483,648 	  to	 2,147,483,647 
// d	0	double		64 bits		1.7 * (10**-308)  to	 1.7 * (10**+308) 
// 

// Taken from the MATLAB V4.2 External Interface Guide
//
// FIXED LENGTH HEADER
// header "MOPT" type flag: given in base 10 as M*1000 + O*100 + P*10 + T*1
//
// 	M(achine) = Me+3
// 		0:	IEEE Little-Endian (PC, x86, DEC Risc)
//		1:	IEEE Big_Endian (Mackintosh, SPARC, Apollo, SGI, HP9000/300)
//		2:	VAX D-float
//		3:	VAX G-float
//		4:	CRAY
//
// 	O = Oe+2
// 		always zero; 'reserved for future use'
//
// 	P(recision) = Pe+1
//		0:	double precision: 64-bit floating point
//		1:	single precision: 32-bit floating point [I have not identified when this is used]
//		2:	32-bit signed integer
//		3:	16-bit signed integer
//		4:	16-bit unsigned integer
//		5:	8-bit\1 byte unsigned integer
//
//	 T(ype of matrix) = Te+0
//		0:	numeric (full) matrix
//		1:	text matrix
//		2:	sparse matrix
//
//	The precision used by the save command depends on the size and type of each matrix:
//
//	Matrices of any noninteger entries & <= 10,000 elements 
//	saved as floating point of 8 bytes per real element.
//	Matrices of all integer entries & > 10,000 elements
//	saved in the following formats, requiring fewer bytes per element.
//
//	[P(3) found to be -32768:32767]
//	[P(4) found to be -2^31:2^31 - 1]
//
//	Element range		Bytes/element	P(recision)	Format 
//	[0:255]			1		5		"uc" unsigned char
//	[0:65535]		2		4		"us" unsigned int
//	[-32767:32767]		2		3		"s"  short_int / int // some confusion between variants
//	[-2^31+1:2^31-1]	4		2		"l"  long
//	other			8		0		"d"  double
//
//	Text matrix elements stored as floating point numbers  (WHY FLOAT?)
//	between 0 and 255 representing ASCII-encoded characters.
//
// header "mrows" integer: number of rows in matrix
//
// header "ncols" integer: number of columns in matrix
//
// header "imagf" integer: complex number matrix flag
//		0:	real only
//		1:	additional imaginary field(s)
//
// header "namelen" integer: 1 + length of matrix name 
//
// VARIABLE LENGTH HEADER
// header "name"	 "namelen"*(ASCII bytes + NULL termination "\0")
//
// header "real"	real elements of matrix according to MO(P)T precision width
//			"mrows"*"ncols" 
//			column1(start)..column1(end),column2(start)..
//
// header "imag"	same as for "real"
//

