home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / ADC_TP3.ZIP / BIGSORT.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1987-03-01  |  14.6 KB  |  557 lines

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