[net.sources] Software Tools in Pascal 6/8

jp@lanl.ARPA (10/06/85)

{
	Copyright (c) 1982
	By:	Chris Lewis

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ Rot -- Rotate a file 90 degrees clockwise }
program Rot;
%include swtools
const
    maxWidth = 2000;
    maxHeight = 130;
var
    buffers: array [1..maxHeight] of array
       [1..maxWidth] of Char;
    i: Integer;
    j: Integer;
    maxReadWidth: Integer;
    maxReadHeight: Integer;
begin
    ToolInit;
    i := 1;
    j := 1;
    maxReadWidth := 0;
    while (GetC(buffers[i,j]) <> ENDFILE) do begin
        if (buffers[i,j] = NEWLINE) then begin
            maxReadWidth := Max(maxReadWidth,j);
            for j := j to maxWidth do
                buffers[i,j] := BLANK;
            j := 1;
            i := i + 1;
        end
        else
            j := j + 1;
        if (i > maxHeight) or (j > maxWidth) then begin
            Message('input file too big');
            leave
        end
    end;
    maxReadHeight := i - 1;
    for i := 1 to maxReadWidth do begin
        for j := maxReadHeight downto 1 do
             PutC (buffers[j,i]);
        PutC (NEWLINE)
    end;
end.
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ SCCopy -- copy string s to cb[i] }
segment SCCopy;
%include swtools
%include defdef
%include defref
%include defproc
procedure SCCopy;
var
    j: Integer;
begin
    j := 1;
    while (s[j] <> ENDSTR) do begin
        cb[i] := s[j];
        j := j + 1;
        i := i + 1
    end;
    cb[i] := ENDSTR
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ SCopy (CMS) -- copy strings }
segment SCopy;
%include swtools
procedure SCopy;
begin
    while(src[i] <> ENDSTR) do begin
        dest[j] := src[i];
        i := i + 1;
        j := j + 1;
    end;
    dest[j] := ENDSTR;
end;
{
	Copyright (c) 1982
	By:	Chris Lewis

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ Screen -- line printer character test }
program Screen;
%include swtools
%include ioref
var i: Integer;
    first: Integer;
begin
ToolInit;
WriteLn(openList[STDOUT].fileVar, '     C H A R A C T E R  S E T');
PutC(NEWLINE);
WriteLn(openList[STDOUT].FileVar,
     '     0 1 2 3 4 5 6 7 8 9 A B C D E F');
for i := 0 to 255 do begin
    if i mod 16 = 0 then begin
        PutC(NEWLINE);
        PutC(NEWLINE);
        first := i div 16;
        if first >= 10 then
            PutC(Chr(first + Ord(BIGA) - 10))
        else
            PutC(Chr(i div 16 + Ord(DIG0)));
        PutC(DIG0);
        PutC(BLANK);
        PutC(BLANK);
    end;
    Write(openList[STDOUT].fileVar, ' ', Chr(i))
end
end.
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ SetBuf -- set Buffer and other Buffer handlers (new-free) }
segment SetBuf;
%include swtools
%include editcons
%include edittype
%include editproc
%include editref
const
    MAXLINES = 10000;
type
    BufType =    { in-memory new/free buffer handler }
        record
            txt: StringPtr;      { text of line }
            mark: Boolean;      { mark for line }
        end;
ref OUTOFSPACE: Boolean;
static heapMark: @ Integer;
static  { This is a PRIVATE buffer }
    intBuff: array [0..MAXLINES] of BufType;
{ SetBuf -- (new-free) initialize line storage Buffer }
procedure SetBuf;
var
    i: 0..MAXLINES;
begin
    Mark(heapMark);
    for i := 0 to MAXLINES do
        intBuff[i].txt := nil;
    curLn := 0;
    lastLn := 0
end;
{ ClrBuf -- (new-free) release storage }
procedure ClrBuf;
var i: 0..MAXLINES;
begin
    Release(heapMark)
end;
{ GetTxt -- (new-free) get text from line n into s }
procedure GetTxt;
begin
    { note: the null is already there }
    if intBuff[n].txt = nil then
        s[1] := ENDSTR
    else
        s := intBuff[n].txt@;
end;
{ PutTxt -- (new-free) put text from lin after curLn }
function PutTxt;
var
    sSize: Integer;
begin
    PutTxt := ERR;
    if (lastLn < MAXLINES) then begin
        lastLn := lastLn + 1;
        sSize := StrLength(lin) + 1;
        if intBuff[lastLn].txt = nil then
            New(intBuff[lastLn].txt, sSize)
        else if (sSize > MaxLength(intBuff[lastLn].txt@)) then begin
            Dispose(intBuff[lastLn].txt);
            New(intBuff[lastLn].txt, sSize)
        end;
        { Check for New failing }
        if OUTOFSPACE then begin
            intBuff[lastLn].txt := nil;  { insurance }
            lastLn := lastLn - 1; { insurance }
            OUTOFSPACE := false;
            Message('out of space, write out and edit again');
            return   { error }
        end;
        WriteStr(intBuff[lastLn].txt@, lin:sSize);
        PutMark(lastLn, false);
        BlkMove(lastLn, lastLn, curLn);
        curLn := curLn + 1;
        PutTxt := OK
    end
end;
{ GetMark -- get mark from nth line }
function GetMark;
begin
    GetMark := intBuff[n].mark
end;
{ PutMark -- put mark m on nth line }
procedure PutMark;
begin
    intBuff[n].mark := m
end;
{ BlkMove -- move block of lines n1..n2 to after n3 }
procedure BlkMove;
begin
    if (n3 < n1-1) then begin
        Reverse (n3+1,n1-1);
        Reverse (n1,n2);
        Reverse (n3+1,n2)
    end
    else if (n3 > n2) then begin
        Reverse(n1,n2);
        Reverse(n2+1,n3);
        Reverse(n1,n3)
    end
end;
{ Reverse -- reverse intBuff[n1]...intBuff[n2] }
procedure Reverse;
var temp: BufType;
begin
    while (n1 < n2) do begin
        temp := intBuff[n1];
        intBuff[n1] := intBuff[n2];
        intBuff[n2] := temp;
        n1 := n1 + 1;
        n2 := n2 - 1
    end
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ SkipBl -- skip blanks and tabs s[i] ... }
segment SkipBl;
%include swtools
%include editcons
%include edittype
%include editproc
procedure SkipBl;
begin
    while (s[i] = BLANK) or (s[i] = TAB) do
        i := i + 1
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ SortDriv -- Driver and Quick sort }
program Sort;
%include SWTOOLS
%include ioref
const
    inCoreSize = 500;
    MERGEORDER = 5;
type
    LineType = -> StringType;
    fdBufType = array [1..MERGEORDER] of FileDesc;
var
    notEof: Boolean;
    inBuf: array [1..inCoreSize] of LineType;
    inFile: fdBufType;
    i: Integer;
    temp: StringType;
    depth: Integer;
    maxDepth: Integer;
procedure GName (n: Integer; var name: StringType);
var
    junk: Integer;
    temp: String(30);
begin
    WriteStr(temp, 'STEMP',n:1,' TEMP A');
    name := temp;
end; {GName}
procedure GOpen (var inFile: fdBufType; f1, f2: Integer);
var
    name: StringType;
    i: 1..MERGEORDER;
begin
    for i := 1 to f2-f1+1 do begin
        GName (f1+i-1, name);
        inFile[i] := MustOpen(name, IOREAD);
    end; {for}
end; {GOpen}
procedure GRemove (var inFile: fdBufType; f1, f2: Integer);
var
    name: StringType;
    i: 1..MERGEORDER;
begin
    for i := 1 to f2-f1+1 do begin
        FClose (inFile[i]);
        GName (f1+i-1, name);
        Remove (name);
    end; {for}
end; {GRemove}
function MakeFile (n: Integer): FileDesc;
var
    name: StringType;
    temp: FileDesc;
begin
    GName (n, name);
    temp := FCreate (name, IOWRITE);
    if temp = IOERROR then
        Error('Could not create temporary file' || Str(name));
    MakeFile := temp;
end; {MakeFile}
procedure PText (nLines: Integer; outFile: FileDesc);
var
    i: Integer;
begin
    for i := 1 to nLines do begin
        PutStr(inBuf[i]@, outFile);
    end; {for}
end; {PText}
function GText (var nLines: Integer; inFile: FileDesc): Boolean;
var
    temp: StringType;
    done: Boolean;
begin
    nLines := 1;
    done := (GetLine(inBuf[nLines]@, inFile, MAXSTR) = false);
    while (not done) do begin
        nLines := nLines + 1;
        if nLines > inCoreSize then leave;
        done := (GetLine(inBuf[nLines]@, inFile, MAXSTR) = false);
    end; {while}
    nLines := nLines - 1;
    GText := done;
end; {GText}

procedure QSort(l,r: integer);
    var i,j: integer;
        temp, hold: LineType;
begin
    if l >= r then return;
    depth := depth + 1;
    maxDepth := Max (maxDepth, depth);
    i := l;
    j := r;
    temp := inBuf[(i+j) div 2];
    repeat
        while inBuf[i]@ < temp@ do
            i := i+1;
        while temp@ < inBuf[j]@ do
            j := j-1;
        if i <= j then begin
            hold := inBuf[i];
            inBuf[i] := inBuf[j];
            inBuf[j] := hold;
            i := i+1;
            j := j-1
        end
    until i > j;
    { if left smaller do: }
    if (j - l) < (r - i) then begin
        QSort(l,j);        {left side first}
        QSort(i,r);
    end
    else begin
        QSort(i,r);        {right side first}
        QSort(l,j);
    end; {if}
    depth := depth - 1;
end {QSort} ;
{ Merge -- Merge infile[1] .. infile[nf] into outfile }
procedure Merge(var inFile: fdBufType; nf: Integer; outFile: FileDesc);
var
    i,j: Integer;
    lbp: Integer;
    temp: LineType;
    fromArray: array [1..MERGEORDER] of Integer;
procedure ReHeap (nf: Integer);
var
    i,j,k: Integer;
    temp: LineType;
begin
    i := 1;
    j := 2 * i;
    while (j <= nf) do begin
        if (j < nf) then { find smaller child }
            if inBuf[j]@ > inBuf[j+1]@ then
                j := j + 1;
        if inBuf[i]@ <= inBuf[j]@ then
            i := nf { proper position found, terminate loop }
        else begin
            k := fromArray[i];
            fromArray[i] := fromArray[j];
            fromArray[j] := k;
            temp := inBuf[i];
            inBuf[i] := inBuf[j];
            inBuf[j] := temp;
        end; {if}
        i := j;
        j := 2 * i;
    end; {while}
end; {while}
procedure PermSort(l,r: Integer);
var
    i,j,k: Integer;
    temp: LineType;
begin
    for i := 1 to r do
        fromArray[i] := i;

    for i := r downto 2 do
        for j := 1 to i-1 do
            if inBuf[j]@ > inBuf[j + 1]@ then begin
                k := fromArray[j];
                fromArray[j] := fromArray[j + 1];
                fromArray[j + 1] := k;
                temp := inBuf[j];
                inBuf[j] := inBuf[j + 1];
                inBuf[j + 1] := temp;
            end; {if}
end; {PermSort}
begin
    j := 1;
    for i := 1 to nf do { get one line from each file }
        if GetLine(inBuf[j]@, inFile[i], MAXSTR) then
            j := j + 1;
    nf := j - 1;
    PermSort (1, nf);   { make initial heap }
    while (nf > 0) do begin
        PutStr(inBuf[1]@, outFile);
        if not
            (GetLine(inBuf[1]@, inFile[fromArray[1]], MAXSTR))
                then begin
            temp := inBuf[1];
            inBuf[1] := inBuf[nf];
            inBuf[nf] := temp;
            fromArray[1] := fromArray[nf];
            nf := nf - 1;
        end; {if}
        ReHeap(nf);
    end; {while}
end; {Merge}

var
    done: Boolean;
    nLines: Integer;
    highMark: Integer;
    lowMark: Integer;
    lim: Integer;
    outFile: FileDesc;
    name: StringType;
begin
    ToolInit;
    highMark := 0;
    for i := 1 to inCoreSize do
        New(inBuf[i]);

    repeat { initial formation of runs }
        done := GText (nLines, STDIN);
        depth := 0;
        maxDepth := 0;
        QSort(1, nLines);
        highMark := highMark + 1;
        outFile := MakeFile(highMark);
        PText (nLines, outFile);
        FClose (outFile);
    until (done);
    lowMark := 1;
    while (lowMark < highMark) do begin { merge runs }
        lim := Min(lowMark +  MERGEORDER - 1, highMark);
        GOpen (inFile, lowMark, lim);
        highMark := highMark + 1;
        outFile := MakeFile(highMark);
        Merge(inFile, lim-lowMark+1, outFile);
        FClose (outFile);
        GRemove (inFile, lowMark, lim);
        lowMark := lowMark + MERGEORDER;
    end; {while}
    GName (highMark, name); { final cleanup }
    outFile := FOpen (name, IOREAD);
    FCopy (outFile, STDOUT);
    FClose (outFile);
    Remove (name);
end.
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ SortDriv -- Driver and Quick sort }
program SortDriv;
%include SWTOOLS
%include ioref
const
    inCoreSize = 500;
type
    LineType = StringPtr;
var
    notEof: Boolean;
    inBuf: array [1..inCoreSize] of LineType;
    i: Integer;
    temp: StringType;
procedure PText (nLines: Integer; outFile: FileDesc);
var
    i: Integer;
begin
    for i := 1 to nLines do
        PutStr (inBuf[i]@, outFile);
end; {PText}
function GText (var nLines: Integer; inFile: FileDesc): Boolean;
var
    i: Integer;
    temp: StringType;
begin
    nLines := 0;
    done := (GetLine(temp, inFile, MAXSTR) = false);
    while (not done) and (nLines < inCoreSize) do begin
        nLines := nLines + 1;
        inBuf[nLines]@ := Str(temp);
        done := (GetLine(temp, inFile, MAXSTR) = false);
    end; {while}
end; {GText}

procedure QSort(l,r: integer);
    var i,j: integer;
        temp, hold: LineType;
begin
    i := l;
    j := r;
    temp := inBuf[(i+j) div 2];
    repeat
        while inBuf[i]@ < temp@ do
            i := i+1;
        while temp@ < inBuf[j]@ do
            j := j-1;
        if i <= j then begin
            hold := inBuf[i];
            inBuf[i] := inBuf[j];
            inBuf[j] := hold;
            i := i+1;
            j := j-1
        end
    until i > j;
    if l < j then
        QSort(l,j);
    if i < r then
        QSort(i,r)
end {QSort} ;
var
    done: Boolean;
    nLines: Integer;
    high: Integer;
    outFile: FileDesc;
begin
    ToolInit;
    high := 0;
    for i := 1 to inCoreSize do
        New(inBuf[i], SizeOf(StringType));
    repeat { initial formation of runs }
        done := GText (nLines, STDIN);
        QSort(1, nLines);
        high := high + 1;
        outFile := MakeFile(high);
        PText (nLines, outFile);
        Close (outFile);
    until (done);
    low := 1;
    while (low < high) do begin { merge runs }
        lim := Min(low +  MERGEORDER - 1, high);
        GOpen (inFile, low, lim);
        high := high + 1;
        outFile := MakeFile(high);
        Merge(inFile, lim-low+1, outFile);
        Close (outFile);
        GRemove (inFile, low, lim);
        low := low + MERGEORDER;
    end; {while}
    GName (high, name) { final cleanup }
    outFile := FOpen (name, IOREAD);
    FCopy (outFile, STDOUT);
    Close (outFile);
    Remove (name);
end.
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ StClose -- insert closure entry at pat[j] }
segment STClose;
%include swtools
%include patdef
procedure StClose;
var
    jp, jt: Integer;
    junk: Boolean;
begin
    for jp := j-1 downto lastJ do begin
        jt := jp + CLOSIZE;
        junk := AddStr(pat[jp], pat, jt, MAXPAT)
    end;
    j := j + CLOSIZE;
    pat[lastJ] := CLOSURE { where original pattern began }
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ StrIndex -- find position of character c in string s }
segment StrIndex;
%include swtools
function StrIndex;
var
    i: Integer;
begin
    i := 1;
    while (s[i] <> c) and (s[i] <> ENDSTR) do
        i := i + 1;
    if (s[i] = ENDSTR) then
        StrIndex := 0
    else
        StrIndex := i
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ StrLength -- determine length of swtools string }
segment StrLength;
%include swtools
function StrLength;
var
    i: Integer;
begin
    i := LBound(s);
    while (s[i] <> ENDSTR) and (i < MAXSTR) do
        i := i + 1;
    StrLength := i - LBound(s)
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ SubLine -- substitute sub for pat in lin and print }
segment SubLine;
%include swtools
%include patdef
%include subdef
%include matchdef
procedure SubLine;
var
    i, lastm, m: Integer;
    junk: Boolean;
begin
    lastm := 0;
    i := 1;
    while (lin[i] <> ENDSTR) do begin
        m := AMatch(lin, i, pat, 1);
        if (m > 0) and (lastm <> m) then begin
            { replace substituted text }
            PutSub(lin, i, m, sub);
            lastm := m
        end;
        if (m = 0) or (m = i) then begin
            { no match or null match }
            PutC(lin[i]);
            i := i + 1
        end
        else        { skip matched text }
            i := m
    end
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ SubSt -- substitute "sub" for occurrences of pattern }
segment SubSt;
%include swtools
%include editcons
%include edittype
%include editproc
%include editref
%include matchdef
%include subdef
function SubSt;
var
    new, old: StringType;
    j, k, lastm, line, m: Integer;
    stat: STCode;
    done, subbed, junk: Boolean;
begin
    if (glob) then
        stat := OK
    else
        stat := ERR;
    done := (line1 <= 0);
    line := line1;
    while (not done) and (line <= line2) do begin
        j := 1;
        subbed := false;
        GetTxt(line, old);
        lastm := 0;
        k := 1;
        while (old[k] <> ENDSTR) do begin
            if (gFlag) or (not subbed) then
                m := AMatch(old, k, pat, 1)
            else
                m := 0;
            if (m > 0) and (lastm <> m) then begin
                { replace matched text }
                subbed := true;
                CatSub(old, k, m, sub, new, j, MAXSTR);
                lastm := m
            end;
            if (m = 0) or (m = k) then begin
                { no match or null match }
                junk := AddStr(old[k], new, j, MAXSTR);
                k := k + 1
            end
            else
                { skip matched text }
                k := m
        end;
        if (subbed) then begin
            if (not AddStr(ENDSTR, new, j, MAXSTR)) then begin
                stat := ERR;
                done := true
            end
            else begin
                stat := LnDelete(line, line, stat);
                stat := PutTxt(new);
                line2 := line2 + curLn - line;
                line := curLn;
                if (stat = ERR) then
                    done := true
                else
                    stat := OK
            end
        end;
        line := line + 1
    end;
    SubSt := stat
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ SW[edit] -- main routine for text editor }
program SW;
%include swtools
%include editcons
%include edittype
%include editproc
var
    curSave, i: Integer;
    status: STCode;
    more: Boolean;
    argIndex: Integer;
def line1: Integer;   { first line number }
def line2: Integer;   { second line number }
def nLines: Integer;  { # lines in buffer }
def curLn: Integer;  { current line: value of dot }
def lastLn: Integer; { last line: value of $ }
def pat: StringType; { pattern }
def lin: StringType; { input line }
def saveFile: StringType; { file name }
value
    line1 := 0;
    line2 := 0;
    nLines := 0;
begin
    ToolInit;
    SetBuf;
    pat[1] := ENDSTR;
    saveFile[1] := ENDSTR;
    i := 1;
    for argIndex := 1 to Nargs do
        if GetArg(argIndex, lin, MAXSTR) then begin
            SCopy (lin, 1, saveFile, i);
            i := StrLength(saveFile) + 2;
            saveFile[i-1] := BLANK
        end;
    i := 1;
    if saveFile[1] <> ENDSTR then
        if (not GetFid(saveFile, i, saveFile)) then
            saveFile[1] := ENDSTR;
    if saveFile[1] <> ENDSTR then
        if (DoRead(0, saveFile) = ERR) then
            Message('Cannot open input file');
    if (OptIsOn(promptFlag)) then begin
        PutC(COLON);
        PutC(NEWLINE)
    end;
    more := GetLine(lin, STDIN, MAXSTR);
    while (more) do begin
        i := 1;
        curSave := curLn;
        if (GetList(lin, i, Status) = OK) then begin
            if (CKGlob(lin, i, status) = OK) then
                status := DoGlob(lin, i, curSave, status)
            else if (status <> ERR) then
                status := DoCmd(lin, i, false, status)
            { else error - do nothing }
        end;
        if (status = ERR) then begin
            Message('eh?');
            curLn := Min(curSave, lastLn)
        end
        else if (status = ENDDATA) then
            more := false;
        { else ok }
        if (more) then begin
            if OptIsOn(promptFlag) then begin
                PutC(COLON);
                PutC(NEWLINE)
            end;
            more := GetLine(lin, STDIN, MAXSTR)
        end
    end;
    ClrBuf
end.
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ Change -- change "from" into "to" on each line }
program swch;
%include swtools
%include patdef
%include matchdef
%include subdef
var
    lin, pat, sub, arg: StringType;
begin
    ToolInit;
    if (not GetArg(1, arg, MAXSTR)) then
        Error('usage: change from <to>');
    if (not GetPat(arg, pat)) then
        Error('change: illegal "from" pattern');
    if (not GetArg(2, arg, MAXSTR)) then
        arg[1] := ENDSTR;
    if (not GetSub(arg, sub)) then
        Error('change: illegal "to" string');
    while (GetLine(lin, STDIN, MAXSTR)) do
        SubLine(lin, pat, sub)
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ Translit -- map characters }
program SWTr;
%include swtools
%include patdef
var
    arg, fromSet, toSet: StringType;
    c: CharType;
    i, lastTo: 0..MAXSTR;
    allBut, squash: Boolean;
{ XIndex -- conditionally invert value from strindex }
function XIndex (var inSet: StringType; c: CharType;
        allBut: Boolean; lastTo: Integer): Integer;
begin
    if (c = ENDFILE) then
        XIndex := 0
    else if (not allBut) then
        XIndex := StrIndex(inSet,c)
    else if (StrIndex(inSet,c) > 0) then
        XIndex := 0
    else
        XIndex := lastTo + 1
end;
begin
    ToolInit;
    if (not GetArg(1, arg, MAXSTR)) then
        Error('usage: translit from to');
    allBut := (arg[1] = NEGATE);
    if allBut then
        i := 2
    else
        i := 1;
    if (not MakeSet(arg, i, fromSet, MaxStr)) then
        Error('translit: "from" set too large');
    if (not GetArg(2,arg, MAXSTR)) then
        toSet[1] := ENDSTR
    else if (not MakeSet(arg, 1, toSet, MAXSTR)) then
        Error('translit: "to" set too large')
    else if (StrLength(fromSet) < StrLength(toSet)) then
        Error('Translit: "from" shorter than "to"');
    lastTo := StrLength(toSet);
    squash := (StrLength(fromSet) > lastTo) or (allBut);
    repeat
        i := XIndex(fromSet, GetC(c), allBut, lastTo);
        if (squash) and (i >= lastTo) and (lastTo > 0) then begin
            PutC(toSet[lastTo]);
            repeat
                i := XIndex(fromSet, GetC(c), allBut, lastTo)
            until (i < lastTo)
        end;
        if (c <> ENDFILE) then begin
            if (i > 0) and (lastTo > 0) then { translate }
                PutC(toSet[i])
            else if (i = 0) then { copy }
                PutC(c)
            { else delete (don't print) }
        end
    until (c = ENDFILE)
end;
{
	Copyright (c) 1981
	By:	Bell Telephone Laboratories, Inc. and
		Whitesmiths, Ltd.,

	This software is derived from the book
		"Software Tools In Pascal", by
		Brian W. Kernighan and P.J. Plauger
		Addison-Wesley, 1981
		ISBN 0-201-10342-7

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ Term -- Evaluate term of arithmetic expression }
segment Term;
%include swtools
%include macdefs
%include macproc
function Term;
var
    v: Integer;
    t: CharType;
begin
    v := Factor(s, i);
    t := GNBChar(s, i);
    while (t in [STAR, SLASH, PERCENT]) do begin
        i := i + 1;
        case t of
            STAR:
                v := v * Factor(s, i);
            SLASH:
                v := v div Factor(s, i);
            PERCENT:
                v := v mod Factor(s, i)
        end {case};
        t := GNBChar(s, i)
    end {while};
    Term  := v
end { Term };
{
	Copyright (c) 1982
	By:	Chris Lewis

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ ToolInit -- (CMS) standard program prologue }
segment ToolInit;
%include swtools
%include iodef
def openList: array [FileDesc] of IOBlock;
def cmdLin: StringType;
def cmdArgs: 0..MAXARG;
def cmdIdx: array [1..MAXARG] of 1..MAXSTR;
def termInput: Boolean;
ref ERRORIO: Boolean;
value
    termInput := false;
procedure ToolInit;
var
    t: 1..MAXSTR;
    i: FileDesc;
    idx: 1..MAXSTR;
    delim: CharType;
    PARMSTRING: String(MAXSTR);
    fileName: StringType;
    cmdLength: 0..MAXSTR;
    redirIn: Boolean;
    j: 1..MAXSTR;
    dummy: StringType;
    okay: Boolean;
    tempArgs: 0..MAXARG;
    XFileName: String(MAXSTR);
    k: 0..MAXSTR;
    nextChar: 1..MAXSTR;
begin
    TermIn(input);
    TermOut(output);
    for i := STDIN to MAXOPEN do
        openList[i].mode := IOAVAIL;
    openList[STDERR].mode := IOWRITE;
    TermOut(openList[STDERR].fileVar);
    PARMSTRING := PARMS;
    if (Length(PARMSTRING) >= 1) and (PARMSTRING[1] = STAR) then begin
        WriteLn('Input Command Parameters:');
        ReadLn(PARMSTRING);
        PARMSTRING := PARMSTRING || SubStr(PARMS, 2, Length(PARMS)-1)
    end;
    for idx := 1 to Length(PARMSTRING) do
        cmdLin[idx] := PARMSTRING[idx];
    cmdLin[Length(PARMSTRING) + 1] := NEWLINE;
    cmdLin[Length(PARMSTRING) + 2] := ENDSTR;
    idx := 1;
    cmdArgs := 0;
    while ((cmdLin[idx] <> ENDSTR) and
      (cmdLin[idx] <> NEWLINE)) do begin
        while (cmdLin[idx] = BLANK) do
            idx := idx + 1;
        if (cmdLin[idx] <> NEWLINE) then begin
            delim := BLANK;
            cmdArgs := cmdArgs + 1;
            if (cmdLin[idx] = SQUOTE) or
              (cmdLin[idx] = DQUOTE) then begin
                cmdIdx[cmdArgs] := idx + 1;
                delim := cmdLin[idx];
                idx := idx + 1
            end
            else
                cmdIdx[cmdArgs] := idx;
            while ((cmdLin[idx] <> NEWLINE) and
              (cmdLin[idx] <> delim)) do
                idx := idx + 1;
            cmdLin[idx] := ENDSTR;
            idx := idx + 1;
        end
    end;
    j := 1;
    tempArgs := cmdArgs;
    while (j <= cmdArgs) do begin
        okay := GetArg(j, dummy, MAXSTR);
        j := j + 1;
        if (dummy[1] = LESS) or (dummy[1] = GREATER) then begin
            if dummy[1] = LESS then
                redirIn := true
            else
                redirIn := false;
            SCopy(dummy, 2, fileName, 1);
            nextChar := StrLength(fileName) + 1;
            tempArgs := tempArgs - 1;
            k := j;
            while (k <= cmdArgs) do begin
                okay := GetArg(k, dummy, MAXSTR);
                k := k + 1;
                if okay and (dummy[1] <> LESS) and
                  (dummy[1]<> GREATER) then begin
                    tempArgs := tempArgs - 1;
                    fileName[nextChar] := BLANK;
                    nextChar := nextChar + 1;
                    SCopy(dummy, 1, fileName, nextChar);
                    nextChar := StrLength(fileName) + 1;
                    j := j + 1;
                end
                else
                    k := cmdArgs + 1;
            end;
            t := 1;
            okay := GetFid(fileName, t, fileName);
            if not okay then
                Error('Bad redirection file name');
            CvtSTS(fileName, XFileName);
            if redirIn then begin
                 openList[STDIN].mode := IOREAD;
                 Reset(openList[STDIN].fileVar, 'NAME=' ||
                     XFileName);
                 termInput := false;
                 if ERRORIO then begin
                     openList[STDIN].mode := IOAVAIL;
                     Error('Cannot open STDIN file');
                     ERRORIO := false
                 end
            end
            else begin
                 openList[STDOUT].mode := IOWRITE;
                 Remove(fileName);
                 ReWrite(openList[STDOUT].fileVar,
                     'LRECL=1000,NAME=' || XFileName);
                 if ERRORIO then begin
                     openList[STDOUT].mode := IOAVAIL;
                     ERRORIO := false
                 end
            end
        end
    end;
    cmdArgs := tempArgs;
    if openList[STDIN].mode = IOAVAIL then begin
        TermIn(openList[STDIN].fileVar);
        openList[STDIN].mode := IOREAD;
        termInput := true;
    end;
    if openList[STDOUT].mode = IOAVAIL then begin
        TermOut(openList[STDOUT].fileVar);
        openList[STDOUT].mode := IOWRITE;
    end;
end;
{
	Copyright (c) 1982
	By:	Chris Lewis

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ Unique -- strip adjacent duplicate lines in a file }
program Unique;
%include swtools
var
    buffer: array [0..1] of StringType;
    bufNum: 0..1;
    sameRecCount: Integer;
    counts: Boolean;
    lastRec: StringType;
begin
    ToolInit;
    buffer[1,1] := ENDSTR;
    buffer[0,1] := NEWLINE;   { just so's they're different }
    lastRec := buffer[1];
    counts := NArgs > 0;
    bufNum := 0;
    sameRecCount := 0;
    while(GetLine(buffer[bufNum], STDIN, MAXSTR)) do begin
        if (not Equal(buffer[0], buffer[1])) then begin
            if counts and (sameRecCount <> 0) then begin
                PutDec(sameRecCount, 6);
                PutC(BLANK)
            end;
            if sameRecCount <> 0 then
                PutStr(lastRec, STDOUT);
            lastRec := buffer[bufNum];
            sameRecCount := 1
        end
        else
            sameRecCount := sameRecCount + 1;
        bufNum := (1 - bufNum)
    end;
    if sameRecCount <> 0 then begin
        if counts then begin
            PutDec(sameRecCount, 6);
            PutC(BLANK)
        end;
        PutStr(lastRec, STDOUT)
    end
end.
{
	Copyright (c) 1982
	By:	Chris Lewis

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ UnRotate -- Unrotate lines rotated by first half of KWIC }
Program UnRotate;
%include swtools
const
    MAXOUT = 80;
    MIDDLE = 40;
    FOLD = DOLLAR;
var
    inBuf, outBuf: StringType;
    tempFile2: FileDesc;
    i, j, f: Integer;
begin
    ToolInit;
    tempFile2 := STDIN;
    while (GetLine(inBuf, tempFile2, MAXSTR)) do begin
        for i := 1 to MAXOUT -1 do
             outBuf[i] := BLANK;
        f := StrIndex(inBuf, FOLD);
        j := MIDDLE - 1;
        for i := StrLength(inBuf)-1 downto f+1 do begin
             outBuf[j] := inBuf[i];
             j := j - 1;
             if (j <= 0) then
                 j := MAXOUT - 1
        end;
        j := MIDDLE + 3;
        for i := 1 to f-1 do begin
             outBuf[j] := inBuf[i];
             j := j mod (MAXOUT - 1) + 1
        end;
        for j := 1 to MAXOUT - 1 do
             if (outBuf[j] <> BLANK) then
                 i := j;
        outBuf[i+1] := ENDSTR;
        PutStr(outBuf, STDOUT);
        PutC(NEWLINE)
    end
end;
{
	Copyright (c) 1982
	By:	Chris Lewis

	Right is hereby granted to freely distribute or duplicate this
	software, providing distribution or duplication is not for profit
	or other commerical gain and that this copyright notice remains 
	intact.
}
{ Wc -- Word Counting program }
program Wc;
%include SWTOOLS
var
    buffer: StringType;
    numChars: Integer;
    numWords: Integer;
    numLines: Integer;
    i: Integer;
    lineLength: Integer;
    inWord: Boolean;
begin
    ToolInit;
    numChars := 0;
    numWords := 0;
    numLines := 0;
    while (GetLine(buffer, STDIN, MAXSTR)) do begin
        inWord := false;
        numLines := numLines + 1;
        lineLength := StrLength (buffer);
        numChars := numChars + lineLength;
        for i := 1 to lineLength do
            if (buffer[i] = BLANK) then
                inWord := false
            else if (not inWord) then begin
                inWord := true;
                numWords := numWords + 1;
            end; {if}
    end; {while}
    PutDec(numChars, 7);
    PutDec(numWords, 7);
    PutDec(numLines, 7);
end; {Wc}