home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / MISC.ZIP / BSORT.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1986-01-01  |  14.6 KB  |  556 lines

  1. {$C-}
  2. {$G512}
  3. {$P512}
  4.  
  5. {*************************************************************************}
  6. {*         Copyright (c) Kim Kokkonen, TurboPower Software, 1985         *}
  7. {*  Released to the public domain for personal, non-commercial use only. *}
  8. {*            Telephone: 408-378-3672, Compuserve 72457,2131             *}
  9. {*                                                                       *}
  10. { sort as large a text file as fits in memory, up to 16000 lines.         }
  11. { only the keys must fit in memory, so use a short key to sort long files.}
  12. { designed as an MSDOS filter, requires Turbo Pascal 3.0 to compile.      }
  13. { written 7/85, modified 1/86 to use indexed textfiles for larger sorts.  }
  14. { see options in WRITEHELP, call BIGSORT with no arguments to list options}
  15. { compile with maximum heap size A000.                                    }
  16. { requires at least 256K free RAM to run as currently configured.         }
  17. { reduce MaxLines to run in smaller space.                                }
  18. {*************************************************************************}
  19.  
  20. PROGRAM bigsort(Input,Output);
  21.  
  22. CONST
  23.  {maxlines*maxlength gives the maximum filesize, here about 4 megabytes}
  24.  MaxLines=16000;{limited by 4*maxlines<=65000}
  25.  MaxLength=255;{max length of a given line, limited to 255}
  26.  
  27.  BufSize=4096;{number of bytes per blockread}
  28.  StackParas=512;{paragraphs to reserve on stack for quicksort}
  29.  convert_high=16777216.0;{used to convert reals to 3 byte small reals}
  30.  convert_med=65536.0;
  31.  convert_low=256.0;
  32.  optiondelim='-';{char used to introduce command line options}
  33.  
  34. TYPE
  35.  lineBuf=STRING[255];
  36.  linePtr=^Byte;
  37.  smallReal=STRING[2];
  38.  lineArray=ARRAY[1..MaxLines] OF linePtr;
  39.  lineArrayPtr=^lineArray;
  40.  positionArray=ARRAY[1..MaxLines] OF Integer;
  41.  TextString=STRING[MaxLength];
  42.  PathName=STRING[64];
  43.  FilePointer=RECORD
  44.               SeekTo:smallReal;
  45.               LenToRead:Byte;
  46.              END;
  47.  FileIndexArray=ARRAY[1..MaxLines] OF FilePointer;
  48.  FileIndexPtr=^FileIndexArray;
  49.  TextBuffer=ARRAY[1..BufSize] OF Char;
  50.  TextBufferPtr=^TextBuffer;
  51.  
  52.  {following record carries all information about the indexed text file}
  53.  {requires 97 bytes in the segment where its var is located}
  54.  {requires 4*maxlines+bufsize on the heap}
  55.  IndexedFile=
  56.  RECORD
  57.   fil:FILE;{untyped file is critical for this application}
  58.   EndOfFile:Boolean;{true when all of file read}
  59.   LineNum:Integer;{last line read in}
  60.   FilePosition:Real;{current byte position in file during readin}
  61.   Buffer:TextBufferPtr;{pointer to buffer for this file}
  62.   BufPos:Integer;{position in current buffer}
  63.   BytesRead:Integer;{number read in last blockread}
  64.   index:FileIndexPtr;{pointer to file index}
  65.  END;
  66.  
  67. VAR
  68.  F:IndexedFile;
  69.  Success:Boolean;
  70.  lines:lineArrayPtr;{pointers to each text line stored here}
  71.  Pos:positionArray;{position of each line after sort}
  72.  nlines:Integer;{number of lines}
  73.  showStats,partial,upper,reverse:Boolean;{option flags}
  74.  numToCopy,beginCol,endCol:Integer;{option values}
  75.  reg:RECORD
  76.       CASE Integer OF
  77.        1:(ax,bx,cx,dx,bp,si,di,ds,es,flags:Integer);
  78.        2:(al,ah,bl,bh,cl,ch,dl,dh:Byte);
  79.      END;
  80.  tstart:Real;
  81.  i,j:Integer;{global variables for recursive sort procedure}
  82.  part:lineBuf;
  83.  
  84.  FUNCTION Time:Real;
  85.   {-return time of day in seconds since midnight}
  86.  BEGIN
  87.   reg.ah:=$2C;
  88.   MsDos(reg);
  89.   Time:=1.0*(reg.dh+60.0*(reg.cl+60.0*reg.ch)+reg.dl/100.0);
  90.  END;{time}
  91.  
  92.  PROCEDURE CheckKeys;
  93.   {-capture ^C, ^S, ^Q}
  94.  VAR
  95.   c:Char;
  96.  BEGIN
  97.   WHILE KeyPressed DO BEGIN
  98.    Read(Kbd,c);
  99.    IF c=^S THEN
  100.     REPEAT
  101.      Read(Kbd,c);
  102.      IF c=^C THEN Halt;
  103.     UNTIL c=^Q
  104.    ELSE IF c=^C THEN Halt;
  105.   END;
  106.  END;{checkkeys}
  107.  
  108.  FUNCTION IOstat(bit:Integer):Boolean;
  109.   {-check status of the standard I/O}
  110.   {bit=0 for input, 1 for output}
  111.   {returns true if I/O is through console}
  112.  VAR
  113.   temp0,temp1:Boolean;
  114.  BEGIN
  115.   reg.ax:=$4400;
  116.   reg.bx:=bit;{standard input or output}
  117.   MsDos(reg);
  118.   temp0:=reg.dx AND 128<>0;
  119.   temp1:=reg.dx AND (1 SHL bit)<>0;
  120.   IOstat:=temp0 AND temp1;
  121.  END;{iostat}
  122.  
  123.  PROCEDURE WriteHelp;
  124.   {-display a help screen}
  125.  BEGIN
  126.   WriteLn(Con);
  127.   WriteLn(Con,'Usage: BIGSORT [Options] <InputPathname [>OutputPathName]');
  128.   LowVideo;
  129.   WriteLn(Con);
  130.   WriteLn(Con,'Sorts text files by line.');
  131.   WriteLn(Con,'Sort limited in size only by available RAM.');
  132.   WriteLn(Con,'Only keys are stored in RAM, specify shorter key for bigger sort.');
  133.   WriteLn(Con,'Each text line limited to 255 characters and must be terminated by a <CR><LF>.');
  134.   WriteLn(Con,'Maximum of 16000 text lines.');
  135.   WriteLn(Con);
  136.   NormVideo;
  137.   WriteLn(Con,'Options:');
  138.   LowVideo;
  139.   WriteLn(Con,'    -I      Ignore case while sorting');
  140.   WriteLn(Con,'    -R      sort in Reverse order');
  141.   WriteLn(Con,'    -Bn     Begin sort key with column n of each line (default 1)');
  142.   WriteLn(Con,'    -En     End sort key with column n of each line (default end of line)');
  143.   NormVideo;
  144.  END;{writehelp}
  145.  
  146.  PROCEDURE SetOptions;
  147.   {-analyze input line}
  148.  VAR
  149.   i,code:Integer;
  150.   Num:STRING[6];
  151.   arg:STRING[64];
  152.  BEGIN
  153.   {set defaults}
  154.   upper:=False;reverse:=False;
  155.   beginCol:=1;endCol:=255;partial:=False;
  156.  
  157.   WriteLn(Con);
  158.  
  159.   {scan through argument list}
  160.   i:=1;
  161.   WHILE i<=ParamCount DO BEGIN
  162.    arg:=ParamStr(i);
  163.    IF (arg[1]=optiondelim) AND (Length(arg)>1) THEN BEGIN
  164.     CASE UpCase(arg[2]) OF
  165.      'I':upper:=True;
  166.      'R':reverse:=True;
  167.      'B':BEGIN
  168.           Num:=Copy(arg,3,6);
  169.           Val(Num,beginCol,code);
  170.           IF code<>0 THEN BEGIN
  171.            WriteLn(Con,'Illegal Begin column specified: ',arg);
  172.            WriteHelp;
  173.            Halt;
  174.           END;
  175.           IF (beginCol<=0) OR (beginCol>255) THEN BEGIN
  176.            WriteLn(Con,'Illegal Begin column specified: ',arg);
  177.            WriteLn(Con,' column must be in the range of 1..255');
  178.            WriteHelp;
  179.            Halt;
  180.           END;
  181.           IF beginCol>1 THEN partial:=True;
  182.          END;
  183.      'E':BEGIN
  184.           Num:=Copy(arg,3,6);
  185.           Val(Num,endCol,code);
  186.           IF code<>0 THEN BEGIN
  187.            WriteLn(Con,'Illegal End column specified: ',arg);
  188.            WriteHelp;
  189.            Halt;
  190.           END;
  191.           IF (endCol<=0) OR (endCol>255) THEN BEGIN
  192.            WriteLn(Con,'Illegal End column specified: ',arg);
  193.            WriteLn(Con,' column must be in the range of 1..255');
  194.            WriteHelp;
  195.            Halt;
  196.           END;
  197.           IF endCol<255 THEN partial:=True;
  198.          END;
  199.     ELSE
  200.      WriteLn(Con,'Unrecognized command line option: ',arg);
  201.      WriteHelp;
  202.      Halt;
  203.     END;
  204.    END ELSE BEGIN
  205.     WriteLn(Con,'Unrecognized command line option: ',arg);
  206.     WriteHelp;
  207.     Halt;
  208.    END;
  209.    i:=Succ(i);
  210.   END;
  211.   numToCopy:=Succ(endCol-beginCol);
  212.   showStats:=NOT(IOstat(1));
  213.  END;{setoptions}
  214.  
  215.  PROCEDURE PutLine(VAR L:lineBuf;VAR lptr:linePtr);
  216.   {-store a string on the heap}
  217.  VAR
  218.   len:Byte ABSOLUTE L;
  219.   tlen:Byte;
  220.   space:Integer;
  221.  BEGIN
  222.   tlen:=Succ(len);{length of string including length byte}
  223.   space:=MaxAvail;
  224.   IF (space<0) OR (space>StackParas) THEN BEGIN
  225.    {enough space left to add string}
  226.    GetMem(lptr,tlen);
  227.    Move(L,lptr^,tlen);
  228.   END ELSE BEGIN
  229.    WriteLn(Con);
  230.    WriteLn(Con,'not enough memory left to store text keys....');
  231.    Halt;
  232.   END;
  233.  END;{putline}
  234.  
  235.  FUNCTION GetLine(lptr:linePtr):lineBuf;
  236.   {-get a string back from the heap}
  237.  VAR
  238.   L:lineBuf;
  239.  BEGIN
  240.   Move(lptr^,L,Succ(lptr^));
  241.   GetLine:=L;
  242.  END;{getline}
  243.  
  244.  PROCEDURE RealToSmall(r:Real;VAR s:smallReal);
  245.   {-convert a real in the range 0..1677215 to a three byte quantity}
  246.  BEGIN
  247.   IF r>=convert_high THEN BEGIN
  248.    WriteLn(Con);
  249.    WriteLn(Con,'real too large to convert to small real');
  250.    WriteLn(Con,r:0:0);
  251.    Halt;
  252.   END;
  253.   s[2]:=Chr(Trunc(r/convert_med));
  254.   r:=r-Ord(s[2])*convert_med;
  255.   s[1]:=Chr(Trunc(r/convert_low));
  256.   r:=r-Ord(s[1])*convert_low;
  257.   s[0]:=Chr(Trunc(r));
  258.  END;{realtosmall}
  259.  
  260.  FUNCTION SmallToReal(VAR s:smallReal):Real;
  261.   {-convert a 3 byte smallreal back to a real}
  262.  BEGIN
  263.   SmallToReal:=
  264.   Int(Ord(s[0]))+convert_low*Int(Ord(s[1]))+convert_med*Int(Ord(s[2]));
  265.  END;{smalltoreal}
  266.  
  267.  FUNCTION Cardinal(i:Integer):Real;
  268.   {-return positive real 0<=r<=65535}
  269.  VAR
  270.   r:Real;
  271.  BEGIN
  272.   r:=i;
  273.   IF r<0 THEN r:=r+65536.0;
  274.   Cardinal:=r;
  275.  END;{cardinal}
  276.  
  277.  PROCEDURE ReadInFile(VAR nlines:Integer);
  278.   {-read lines from standard input and put the keys on the heap}
  279.  VAR
  280.   L:lineBuf;
  281.  
  282.   PROCEDURE OpenFile(fname:PathName;
  283.                      VAR F:IndexedFile;
  284.                      VAR Success:Boolean);
  285.    {-open an indexed textfile, return true if successful}
  286.   BEGIN
  287.    WITH F DO BEGIN
  288.  
  289.     {open the physical file}
  290.     Assign(fil,fname);
  291.     {$I-}Reset(fil,1){$I+};
  292.     Success:=(IOResult=0);
  293.     IF NOT(Success) THEN Exit;
  294.     EndOfFile:=False;
  295.  
  296.     {allocate the file buffer}
  297.     Success:=16.0*Cardinal(MaxAvail)>Cardinal(SizeOf(TextBuffer));
  298.     IF NOT(Success) THEN Exit;
  299.     New(Buffer);
  300.     BytesRead:=0;
  301.     BufPos:=1;{force blockread the first time}
  302.  
  303.     {allocate the file index array}
  304.     Success:=16.0*Cardinal(MaxAvail)>Cardinal(SizeOf(FileIndexArray));
  305.     IF NOT(Success) THEN Exit;
  306.     New(index);
  307.     LineNum:=0;
  308.     FilePosition:=0.0;
  309.  
  310.    END;
  311.   END;{openfile}
  312.  
  313.   PROCEDURE ReadNewLine(VAR F:IndexedFile;VAR L:lineBuf);
  314.    {-read a text line and store information for later random access}
  315.   VAR
  316.    EndOfLine:Boolean;
  317.    lpos,terminators:Integer;
  318.    ch:Char;
  319.    sm:smallReal;
  320.   BEGIN
  321.    WITH F DO BEGIN
  322.     EndOfLine:=False;
  323.     lpos:=0;
  324.     terminators:=1;
  325.  
  326.     {look at characters until end of line found}
  327.     REPEAT
  328.  
  329.      IF BufPos>BytesRead THEN BEGIN
  330.       {get another buffer full}
  331.       BlockRead(fil,Buffer^,BufSize,BytesRead);
  332.       BufPos:=1;
  333.      END;
  334.  
  335.      IF BytesRead=0 THEN
  336.       ch:=^Z
  337.      ELSE BEGIN
  338.       ch:=Buffer^[BufPos];
  339.       BufPos:=Succ(BufPos);
  340.      END;
  341.  
  342.      CASE ch OF
  343.       ^M:terminators:=Succ(terminators);
  344.       ^J:EndOfLine:=True;
  345.       ^Z:BEGIN
  346.           EndOfLine:=True;
  347.           EndOfFile:=True;
  348.          END;
  349.      ELSE
  350.       IF lpos<MaxLength THEN BEGIN
  351.        lpos:=Succ(lpos);
  352.        L[lpos]:=ch;
  353.       END;
  354.      END;
  355.  
  356.     UNTIL EndOfLine;
  357.  
  358.     {finish up line}
  359.     L[0]:=Chr(lpos);
  360.  
  361.     {store info for later random access}
  362.     IF LineNum<MaxLines THEN BEGIN
  363.      LineNum:=Succ(LineNum);
  364.      WITH index^[LineNum] DO BEGIN
  365.       RealToSmall(FilePosition,sm);
  366.       Move(sm,SeekTo,3);
  367.       LenToRead:=lpos;
  368.      END;
  369.      FilePosition:=FilePosition+lpos+terminators;
  370.     END;
  371.  
  372.    END;
  373.   END;{readnewline}
  374.  
  375.   PROCEDURE GetSortKey(VAR L:lineBuf);
  376.    {-return the sort key for the text line l}
  377.   VAR
  378.    i:Integer;
  379.   BEGIN
  380.    IF partial THEN
  381.     L:=Copy(L,beginCol,numToCopy);
  382.    IF upper THEN
  383.     FOR i:=1 TO Length(L) DO L[i]:=UpCase(L[i]);
  384.    IF reverse THEN
  385.     FOR i:=1 TO Length(L) DO L[i]:=Chr(255-Ord(L[i]));
  386.   END;{getsortkey}
  387.  
  388.  BEGIN
  389.   nlines:=0;
  390.   {set up the indexed file data structure}
  391.   OpenFile('INP:',F,Success);
  392.   IF NOT(Success) THEN BEGIN
  393.    WriteLn(Con);
  394.    WriteLn(Con, 'could not set up indexed file data structure....');
  395.    Halt;
  396.   END;
  397.  
  398.   WHILE NOT F.EndOfFile DO BEGIN
  399.    {read line}
  400.    ReadNewLine(F,L);
  401.    GetSortKey(L);
  402.    IF nlines<MaxLines THEN BEGIN
  403.     nlines:=Succ(nlines);
  404.     IF (nlines AND 63=0) THEN
  405.      Write(Con,^H^H^H^H^H,nlines:5);
  406.     CheckKeys;
  407.     {store key on text heap}
  408.     PutLine(L,lines^[nlines]);
  409.     {initialize the pos array}
  410.     Pos[nlines]:=nlines;
  411.    END ELSE BEGIN
  412.     WriteLn(Con);
  413.     WriteLn(Con,'Exceeded maximum number of lines....');
  414.     Halt;
  415.    END;
  416.   END;
  417.  END;{readinfile}
  418.  
  419.  PROCEDURE SortData(l,r:Integer);
  420.   {-recursive quicksort}
  421.  VAR
  422.   partpos:Integer;
  423.  
  424.   PROCEDURE WriteStatus(i,j:Integer);
  425.    {-provide some reassurance that sort is proceeding}
  426.   BEGIN
  427.    Write(Con,^H^H^H^H^H);ClrEol;
  428.    {prints size of current partition being sorted}
  429.    Write(Con,(j-i):5);
  430.   END;{writestatus}
  431.  
  432.   PROCEDURE Swap(i,j:Integer);
  433.    {-swap the two referenced data elements}
  434.   VAR
  435.    t:Integer;
  436.   BEGIN
  437.    t:=Pos[i];
  438.    Pos[i]:=Pos[j];
  439.    Pos[j]:=t;
  440.   END;{swap}
  441.  
  442.  BEGIN
  443.  
  444.   IF l<r THEN BEGIN
  445.  
  446.    i:=l;
  447.    j:=Succ(r);
  448.    IF (j-i)>50 THEN WriteStatus(i,j);
  449.  
  450.    {get a random partitioning element}
  451.    Swap(i,i+Random(j-i));
  452.    part:=GetLine(lines^[Pos[i]]);
  453.  
  454.    {swap elements until all less than partition are to left, etc.}
  455.    REPEAT
  456.     REPEAT
  457.      i:=Succ(i);
  458.     UNTIL (i>j) OR (GetLine(lines^[Pos[i]])>=part);
  459.     REPEAT
  460.      j:=Pred(j);
  461.     UNTIL (GetLine(lines^[Pos[j]])<=part);
  462.     IF i<j THEN Swap(j,i);
  463.    UNTIL i>=j;
  464.  
  465.    Swap(l,j);
  466.    partpos:=j;
  467.    SortData(l,Pred(partpos));
  468.    SortData(Succ(partpos),r);
  469.   END;
  470.  
  471.  END;{sortdata}
  472.  
  473.  PROCEDURE WriteOutFile(nlines:Integer);
  474.   {-write out the sorted information}
  475.  VAR
  476.   i:Integer;
  477.   L:lineBuf;
  478.  
  479.   PROCEDURE ReadIndexedLine(VAR F:IndexedFile;
  480.                             Num:Integer;
  481.                             VAR L:TextString);
  482.    {-get an indexed line from f}
  483.   BEGIN
  484.    WITH F DO
  485.     WITH index^[Num] DO BEGIN
  486.      LongSeek(fil,SmallToReal(SeekTo));
  487.      BlockRead(fil,L[1],LenToRead);
  488.      L[0]:=Chr(LenToRead);
  489.     END;
  490.   END;{readindexedline}
  491.  
  492.  BEGIN
  493.   IF NOT(upper OR partial OR reverse) THEN
  494.    {take the output directly from memory}
  495.    FOR i:=1 TO nlines DO BEGIN
  496.     WriteLn(GetLine(lines^[Pos[i]]));
  497.     IF showStats AND (i AND 63=0) THEN
  498.      Write(Con,^H^H^H^H^H,i:5);
  499.     CheckKeys;
  500.    END
  501.   ELSE
  502.    {use the indexed text file}
  503.    FOR i:=1 TO nlines DO BEGIN
  504.     ReadIndexedLine(F,Pos[i],L);
  505.     IF L<>'' THEN WriteLn(L);
  506.     IF showStats AND (i AND 15=0) THEN
  507.      Write(Con,^H^H^H^H^H,i:5);
  508.     CheckKeys;
  509.    END;
  510.  END;{writeoutfile}
  511.  
  512. BEGIN{main}
  513.  
  514.  IF IOstat(0) THEN BEGIN
  515.   WriteLn(Con);
  516.   WriteLn(Con,'input must be redirected from a file....');
  517.   WriteHelp;
  518.   Halt;
  519.  END;
  520.  
  521.  {analyze command line options}
  522.  SetOptions;
  523.  
  524.  {get the space for lines array}
  525.  IF 16.0*Cardinal(MaxAvail)<Cardinal(SizeOf(lineArray)) THEN BEGIN
  526.   WriteLn(Con,'not enough memory for line pointer array....');
  527.   Halt;
  528.  END;
  529.  New(lines);
  530.  
  531.  tstart:=Time;
  532.  Write(Con,0.0:7:2,' READING ',1:5);
  533.  
  534.  {read in the input file}
  535.  ReadInFile(nlines);
  536.  
  537.  Write(Con,^H^H^H^H^H);WriteLn(Con,'Total lines: ',nlines);
  538.  
  539.  {sort}
  540.  Write(Con,(Time-tstart):7:2,' SORTING ','':5);
  541.  
  542.  SortData(1,nlines);
  543.  
  544.  Write(Con,^H^H^H^H^H);ClrEol;WriteLn(Con);
  545.  Write(Con,(Time-tstart):7:2,' WRITING ',1:5);
  546.  IF NOT(showStats) THEN WriteLn(Con);
  547.  
  548.  {write out the results}
  549.  WriteOutFile(nlines);
  550.  
  551.  IF showStats THEN Write(Con,^H^H^H^H^H);
  552.  WriteLn(Con,'Total lines: ',nlines);
  553.  WriteLn(Con,(Time-tstart):7:2,' DONE ');
  554.  
  555. END.
  556.