DeTab is the inverse of EnTab. Here is the specification:
{
PROGRAM
DeTab -- convert tabs to blanks
USAGE
DeTab
FUNCTION
DeTab copies its input to its output, expanding horizontal tabs to
blanks along the way, so that the output is visually the same as the
input, but contains no tab chars. Tab stops are assumed to be set
every three columns (i.e., 1, 4, 7, 10, ...), so that each tab char
is replaced by from one to three blanks.
BUGS
1. DeTab is naive about backspace, vertical motions, and
non-printing chars.
2. Each line of output will be truncated at MAXSTR chars.
3. Trailing BLANKS will be trimmed from each line of output.
}
DeTab's main routine echos EnTab's, reducing the problem to one of detabbing a single string. To detab the string we could try to simply apply EnTab's strategy in reverse, however, this is another chance to apply the "how would a human do it?" approach.
How would a human do it? If a human is sitting at a typewriter typing the incoming text, what is happening? First, he is looking at a blank sheet of paper. At least the current line is blank. If he hits any other than the TAB key the corresponding letter/character gets typed. Even if he hits the space bar a character, a BLANK, gets typed, even though what actually happens is that the carriage moves one space to the left (effectively moving the print head to the right). However, if he hits the TAB key the carriage moves to the left until it hits a tab stop, printing nothing and leaving the print head at the tab stop awaiting the next character.
This is the behavior we will try to emulate. We will copy the input string to an output string. But first we will fill the output string with BLANKS, effectively giving us a blank line. We will use an index
i for the input string and
j for the output string. If the char is not a TAB then we will copy the char and increment both
i and
j. However, if it is a TAB we increment
i once but we increment
j repeatedly until we get to a tab stop.
To deal with the situation of what happens when we encounter a TAB after the last tab stop we cease incrementing
j once it exceeds MAXSTR. We also do an early exit from the routine should
j exceed MAXSTR. In keeping with our previous philosophy we do not expect this to happen but it could. If it does the program exits cleanly, albeit with a corrupted output. We will simply live with this known bug.
Finally, we trim the end of the output string of any BLANKS. This means that any trailing BLANKS in the input string will not get copied to the output. Strictly speaking this is a bug but it is unlikely to cause problems. Again we will live with it.
The function
RTrim gets rid of trailing BLANKS. We will add it to
Tools. While we are at it we will also add
LTrim, which gets rid of leading BLANKS, and
Trim, which does both. I will post the
code next time.
Program DeTab ;
{
DeTab -- replace blanks with tabs and blanks
}
Uses
Tools,
TabsUnit ;
Procedure DeTabStr (Var S : String) ;
{
Detab a string
}
Var
i : Byte ;
j : Word ;
T : String ;
begin { DeTabStr }
{
clear target string
}
T := '' ;
while Length(T) < MAXSTR do
T := T + BLANK ;
j := 1 ;
for i := 1 to Length(S) do begin
if j > MAXSTR then { not likely but not impossible }
Break ;
Case S[i] of
TAB :
repeat
j := j + 1
until (j in TabSet) OR (j > MAXSTR)
else
begin
if j <= MAXSTR then
T[j] := S[i] ;
j := j + 1
end
end { Case }
end ;
S := RTrim(T)
end ; { DeTabStr }
Var
S : String ;
begin { DeTab }
SetTabs ;
while NOT eof do begin
ReadLn (S) ;
DeTabStr (S) ;
WriteLn (S)
end
end. { DeTab }