{$LINESIZE:130,$TITLE:'Detab  -  a file'}
{$SYMTAB-}
{$DEBUG-}
{
+*************************************************************+
|                                                             |
|                     D E T A B                               |
|                                                             |
|    This program removes tabs from a file.                   |
|                                                             |
|    This syntax is:                                          |
|      DETAB FileName /T#                                     |
|                                                             |
|       # is the tab value; the default value is 8.           |
|                                                             |
|       FileName is the input file.                           |
|       FileName may be a DOS 2.0 path name.                  |
|       Output is directed to standard out.                   |
|                                                             |
|       Enterring just  DETAB as a command will cause         |
|       output of this explanation.                           |
|                                                             |
+*************************************************************+
|                  Copyright (c) 1983                         |
|                    Ray Turner                               |
|              Fountain Valley, California                    |
|                                                             |
|       Permission to copy for non-commercial use is          |
|       freely granted and even encouraged.                   |
|                                                             |
+*************************************************************+
}
{

						L I M I T A T I O N S


The program currently requires that the file end with a CR to 
 satisfy a READLN; else a Pascal runtime error will occur.

			******************************************

							N O T E S

The tab algorithm matches that of The Final Word; other editors
 may  be different. This may be alterred in DeTabs. The algorithm is: 

Text following a tab begins in the column following the next tab
 column, where tab columns are at n, 2n, 3n, ... if  n  is the
 tab value enterred on the comand line. Thus col. 4 would contain
 the text following a tab as the first character on a line.

This program uses a DOS interface package written in assembler,
 which is also included. 


			******************************************

}

{$PAGE+}
{
+*************************************************************+
|                                                             |
|                          D E T A B                          |
|                                                             |
+*************************************************************+
}

PROGRAM DETAB ( Input, Output );


function GetNextArg(var SwitchType,Numeric:boolean;var SwitchChar:char;
			var StringArg:lstring;var NumericArg:integer):boolean;external;
			{ returns next argument from command line }
			{ returns true if there was an argument }
			{ arguments are fields separated by spaces or a slash (/) }
{			SwitchType      true if argument begins with a slash
			Numeric         true if argument is an integer
			SwitchChar      char following / for switch type args
								 (SwitchChar is folded to upper case)
			StringArg       contains the ASCII argument (not the /)
			NumericArg      contains the value of arg, if applicable

			double quotes ("...") must be used to enclose an arg 
				containing a space or slash (/). 
}


VAR
 i          : integer;
 ch         : char;
 Tabs       : integer;		{ tab stop value }
 LineNum    : integer;     		{ current line number }
 OutLine    : Lstring(255);	{ De-tabbed string to output }
 Line       : Lstring(132);	{ string to hold lines being read in }
 CommandError : boolean;
 FileSpec   : Lstring(64); 	{ File specifier from command line }
 InputFile  : text; 


{*************************************************************}
{                                                             }
{             C O N V E R T  T A B S   to  S P A C E S        }
{                                                             }
{*************************************************************}

	PROCEDURE DeTabs (Tabval:integer;var InString,OutString:Lstring;
							limit:integer);
{
	DeTabs converts tab characters into the appropriate number of spaces
	while moving the string from InString to OutString.
   limit is the maximum length of OutString. 
	TabVal is the number of columns between tab stops.
}

		var nt,i,j,len  : integer;
			 ch,tab  : char;

		BEGIN
			len := ord(InString.len);
			tab := chr(9);			{horizontal tab char.}
			j := 1;
			OutString[0] := chr(0);		{init. string to null}

			for i := 1 to len do begin
				ch := InString[i];
				if ch = tab then begin 

					{ magic formula matching Final Word tabs }
					nt := Tabval - ( (j - 1) MOD Tabval);

					while nt > 0 do begin
						concat (OutString,' ');
						j := j + 1;
						if j >= limit then BREAK;
						nt := nt - 1
					end;   {while}
				end
				else { not a tab char } begin
					j := j + 1;
					if j >= limit then BREAK;
					concat(OutString,ch)
				end {if}
			end; {for}
		END;     {DeTab}

{$page+,$subtitle:'directions'}

 PROCEDURE PrintDirections;  { print directions if no args given }
   begin

writeln('+----------------------------------------------------------+');
writeln('|                                                          |');
writeln('|                     D E T A B                            |');
writeln('|                                                          |');
writeln('|    This program expands tabs into spaces.                |');
writeln('|                                                          |');
writeln('|    This syntax is:                                       |');
writeln('|      DETAB FileName /T#                                  |');
writeln('|                                                          |');
writeln('|      # is the tab value; the default is 8.               |');
writeln('|                                                          |');
writeln('|       FileName is the input file.                        |');
writeln('|       FileName may be a DOS 2.0 path name.               |');
writeln('|       Output is to standard output.                      |');
writeln('|                                                          |');
writeln('+----------------------------------------------------------+');
writeln('|                  Copyright (c) 1983                      |');
writeln('|                    Ray Turner                            |');
writeln('|              Fountain Valley, California                 |');
writeln('+----------------------------------------------------------+');
writeln;
CommandError := TRUE; { to stop any attemp at processing }
END;



PROCEDURE Asciiz (var str:lstring);		{add a null byte to end of string}
begin
	concat(str,chr(0));
end;


PROCEDURE MoveUntil (var ToString:lstring;var FromString:lstring;
							Fstart:integer;ch:char;limit:integer);
	 {move chars from one string to another until a 
								spec'd char is encountered.}
	 {chars are concatenated onto ToString from FromString[Fstart]. 
     Limit sets a max on scan. 
	  FromString is VAR just for efficiency sake.}

var i,count : integer;
begin
	count := scaneq(limit,ch,FromString,Fstart); { = # chars. skipped }
	for i := 0 to (count - 1) do
		concat(ToString,FromString[Fstart + i]);
end;



{$PAGE+,$SUBTITLE:'Process Command Line'}
{
***************************************************************
|                                                             |
|                  C O M M A N D S                            |
|                                                             |
|   This procedure retrieves the parameters passed to DETAB   |
|   from the user command line and interprets them.           |
|                                                             |
***************************************************************
}

PROCEDURE Commands;

VAR
	SwitchType,Numeric : boolean;
	SwitchChar : char;
	StringArg : Lstring(80);
	NumericArg : integer;


BEGIN               					      {COMMANDS}
  	CommandError := FALSE;

	if not GetNextArg(SwitchType,Numeric,SwitchChar,FileSpec,NumericArg)
		then { null command line } PrintDirections
	else if SwitchType then begin
		CommandError := true;
		writeln('Missing, misplaced, or invalid File Specifier');
	end else begin { get tab # arg if any }
      if GetNextArg(SwitchType,Numeric,SwitchChar,StringArg,NumericArg)
			then Tabs := NumericArg
		else Tabs := 8; 
	end; {else}
END;  		{ Commands }

{$PAGE+,$subtitle:'Main Program'}

{*************************************************************}
{                                                             }
{                  T H E   P R O G R A M                      }
{                                                             }
{*************************************************************}

BEGIN
	Commands;			{ read and interpret command line }
	if not CommandError then begin { detab the stuff }
		LineNum := 0;
		assign(InputFile,FileSpec);
		reset(InputFile);

		While not eof(InputFile) do begin
			readln(InputFile,Line);
			LineNum := LineNum +1;
			DeTabs(Tabs,Line,OutLine,250);		{convert tabs to blanks}
			writeln(OutLine);
		end;
	end; { if }

END.
