home *** CD-ROM | disk | FTP | other *** search
/ HomeWare 14 / HOMEWARE14.bin / utils2 / merge215.arj / MERGE115.PAS < prev    next >
Pascal/Delphi Source File  |  1994-04-20  |  12KB  |  425 lines

  1. (* MERGEFILE v1.15  4/20/94
  2.  
  3.    MERGE v1.15 released to the public domain on 4/20/94 by the author.  The
  4.    code may not be exemplary, but the original was written in a hurry (which
  5.    is also my excuse for why there are not any comments) by someone who has
  6.    not used Pascal in a LONG, LONG TIME.  I probably won't spend much more
  7.    time fixing this one up either; the shareware version will get most of my
  8.    attention.  I'm supplying the code in case someone actually wants to play
  9.    around with it or to provide a humorous diversion for the serious
  10.    programmer.  I've tried to find all the bugs, but you never really know.
  11.    If you have any comments or find a problem with this or the shareware
  12.    version, I'd like to hear from you.  You're not required to be
  13.    "registered."  You can contact me at the following address:
  14.  
  15.                                  Hal Parks
  16.                                  404B W. Oak
  17.                                  Carbondale, IL 62901
  18.  
  19.    See the HISTORY.DOC that should accompany this program for comments on the
  20.    revisions.
  21.  
  22.    Happy merging!                                                          *)
  23.  
  24. program merge;
  25.  
  26. uses crt, dos;
  27.  
  28. const
  29.    MinPar   = 1;
  30.    MaxPar   = 5;
  31.    MaxOpt   = 2;
  32.    MinFil   = 1;
  33.    MaxFil   = 3;
  34.    MaxStr   = 255;
  35.    ProgName = '   MERGEFILE v1.15.  Released to Public Domain by author Hal Parks (4/20/94)';
  36.    HelpMsg  = 'For help, enter MERGE without any parameters.';
  37.  
  38. type
  39.    namstrg  = string[MaxStr];
  40.    namearr  = array[1..MaxFil] of namstrg;
  41.  
  42. var
  43.    ScrnOut,               (* so screen output can be redirected *)
  44.    List1In,
  45.    List2In,
  46.    MergeOut : text;
  47.    Line1,
  48.    Line2,
  49.    Line1C,
  50.    Line2C,
  51.    LastLine,
  52.    FName    : namstrg;
  53.    FNames   : namearr;
  54.    ErCode,
  55.    FilCnt,
  56.    OptCnt   : integer;
  57.    Ascend,
  58.    CaseSen,
  59.    NoDup    : boolean;
  60.  
  61. function Upper(name:namstrg) : namstrg;
  62.  
  63. var x:integer;
  64.  
  65. begin
  66.    for x := 1 to length(name) do
  67.       name[x] := upcase(name[x]);
  68.    Upper := name
  69. end; { function Upper }
  70.  
  71. procedure Help;
  72.  
  73. begin
  74.    writeln(ScrnOut);
  75.    writeln(ScrnOut, ProgName);
  76.    writeln(ScrnOut);
  77.    writeln(ScrnOut, '       Syntax:  MERGE FileName1 [FileName2] [FileName3] [/C] [/D] [/K]');
  78.    writeln(ScrnOut);
  79.    writeln(ScrnOut, '   FileName1 and FileName2 are the input files and must both be sorted lists.');
  80.    writeln(ScrnOut, '   The merged list is written to FileName3 if it is given (the input files');
  81.    writeln(ScrnOut, '   are unchanged), otherwise the last file name serves as both an input and');
  82.    writeln(ScrnOut, '   the output file and is overwritten.  Entering only one file name "merges"');
  83.    writeln(ScrnOut, '   the file on itself (e.g., to remove duplicates from a sorted list).  MERGE');
  84.    writeln(ScrnOut, '   defaults to case sensitive in ascending order ("H" will appear before');
  85.    writeln(ScrnOut, '   "h"), using the entire input line as the merge key, and discarding any');
  86.    writeln(ScrnOut, '   duplicates in either or both input files.  Entering "/C" (without the');
  87.    writeln(ScrnOut, '   quotes) on the command line results in the merge not being case sensitive');
  88.    writeln(ScrnOut, '   and "/K" will keep all duplicates.  Using "/D" will cause the merge to be');
  89.    writeln(ScrnOut, '   in descending order (the lists must also be sorted in descending order).');
  90.    writeln(ScrnOut, '   Lines up to 255 characters can be merged.  If the files to merge are not');
  91.    writeln(ScrnOut, '   in the current directory, the full path must be entered with the file');
  92.    writeln(ScrnOut, '   name.  MERGE does not need to be in the same directory as the files if it');
  93.    writeln(ScrnOut, '   is on the path or invoked by specifying its directory.  File names and');
  94.    writeln(ScrnOut, '   options are not case sensitive and can be entered in any sequence.');
  95.    writeln(ScrnOut, '   Entering MERGE without any parameters will display this HELP screen.');
  96.    writeln(ScrnOut);
  97.    writeln(ScrnOut, 'Happy Merging! ');
  98.    close(ScrnOut);
  99.    HALT(0)
  100. end; { procedure Help }
  101.  
  102. procedure Error;
  103.  
  104. begin
  105.    writeln(ScrnOut);
  106.  
  107.    case ErCode of
  108.      8 : writeln(ScrnOut, 'Aborting - "', LastLine, '" is not a valid option.');
  109.  
  110.      7 : writeln(ScrnOut, 'Aborting - there is a maximum of three filenames.');
  111.  
  112.      6 : writeln(ScrnOut, 'Aborting - no filename specified.');
  113.  
  114.      5 : writeln(ScrnOut, 'Aborting - incorrect use of parameters and/or options.');
  115.  
  116.      4 : writeln(ScrnOut, 'Aborting - too many parameters on the command line.');
  117.  
  118.   1, 3 : begin
  119.             writeln(ScrnOut, 'Aborting - "', FNames[1], '" not found.');
  120.             if (ErCode = 3) then
  121.                writeln(ScrnOut, '         - "', FNames[2], '" not found.')
  122.          end;
  123.  
  124.      2 : writeln(ScrnOut, 'Aborting - "', FNames[2], '" not found.')
  125.    end; { case ErCode }
  126.  
  127.    writeln(ScrnOut);
  128.    writeln(ScrnOut, HelpMsg);
  129.    close(ScrnOut);
  130.    HALT(ErCode)
  131. end; { procedure Error }
  132.  
  133. procedure Parse;
  134.  
  135. var x:integer;
  136.  
  137. function OptionC(option:namstrg) : boolean;
  138.  
  139. begin
  140.    if option = '/C' then OptionC := true
  141.     else OptionC := false
  142. end; { OptionC }
  143.  
  144. function OptionD(option:namstrg) : boolean;
  145.  
  146. begin
  147.    if option = '/D' then OptionD := true
  148.     else OptionD := false
  149. end; { OptionD }
  150.  
  151. function OptionK(option:namstrg) : boolean;
  152.  
  153. begin
  154.    if option = '/K' then OptionK := true
  155.     else OptionK := false
  156. end; { OptionK }
  157.  
  158. (* So there is a comment or two.  I had to put this function in because while
  159.    playing around with the program I found that if you stuck in an invalid
  160.    option switch, the code would try to find a file by that name and strange
  161.    things could happen. *)
  162.  
  163. function NotOption(option:namstrg) : boolean;
  164.  
  165. begin
  166.    if pos('/',option) = 1 then NotOption := true
  167.     else NotOption := false
  168. end; { NotOption }
  169.  
  170. begin { procedure Parse }
  171.    x := 0;
  172.    FilCnt := 0;
  173.    OptCnt := 0;
  174.    NoDup := true;
  175.    Ascend := true;
  176.    CaseSen := true;
  177.  
  178.    if paramcount > MaxPar then ErCode := 4
  179.     else
  180.  
  181.       while (x < paramcount) and (ErCode = 0) do
  182.       begin
  183.          x := x + 1;
  184.          FName := Upper(paramstr(x));
  185.  
  186.          if OptionC(FName) then
  187.          begin
  188.             OptCnt := OptCnt + 1;
  189.             CaseSen := false
  190.          end
  191.  
  192.           else if OptionD(FName) then
  193.          begin
  194.             OptCnt := OptCnt + 1;
  195.             Ascend := false
  196.          end
  197.  
  198.           else if OptionK(FName) then
  199.          begin
  200.             OptCnt := OptCnt + 1;
  201.             NoDup := false
  202.          end
  203.  
  204.           else if NotOption(FName) then
  205.          begin
  206.             LastLine := FName;
  207.             ErCode := 8
  208.          end
  209.  
  210.           else
  211.          begin
  212.             FilCnt := FilCnt + 1;
  213.             if FilCnt <= MaxFil then FNames[FilCnt] := FName
  214.              else ErCode := 7
  215.          end { if else }
  216.       end; { while }
  217.  
  218.    if ErCode = 0 then
  219.       if OptCnt > MaxOpt then ErCode := 5
  220.        else if FilCnt < MinFil then ErCode := 6;
  221.  
  222.    if ErCode > 0 then Error
  223. end; { procedure Parse }
  224.  
  225. procedure GetParam;
  226.  
  227. var path : dirstr;
  228.     name : namestr;
  229.      ext : extstr;
  230.  
  231. function FileExists(var fil:text) : boolean;
  232.  
  233. begin
  234.    {$i-} reset(fil); close(fil); {$i+}
  235.    FileExists := (IoResult=0)
  236. end; { FileExists }
  237.  
  238. procedure OutExists(name:namstrg);
  239.  
  240. var ch:char;
  241.  
  242. begin
  243.    writeln;
  244.    write('"', name, '" exists.  Overwrite it? (Y/N) ');
  245.    ch := readkey;
  246.    writeln(ch);
  247.    if upcase(ch) <> 'Y' then
  248.    begin
  249.       close(ScrnOut);
  250.       HALT(0)
  251.    end
  252. end; { OutExists }
  253.  
  254. begin { procedure GetParam }
  255.    Parse;
  256.  
  257. (* use Turbo Pascal FEXPAND function to store the full path for "FName" *)
  258.  
  259.    FNames[1] := FEXPAND(FNames[1]);
  260.    assign(List1In, FNames[1]);
  261.    if not FileExists(List1In) then ErCode := 1
  262.     else if FilCnt = 1 then OutExists(FNames[1]);
  263.  
  264.    if FilCnt > 1 then
  265.    begin
  266.       FNames[2] := FEXPAND(FNames[2]);
  267.       assign(List2In, FNames[2]);
  268.       if not FileExists(List2In) then ErCode := ErCode + 2
  269.        else if (FilCnt = 2) and (ErCode = 0) then OutExists(FNames[2])
  270.    end;
  271.  
  272.    if ErCode > 0 then Error;
  273.  
  274. (* oops, a little problem I overlooked when I got rid of the CLEANUP
  275.    procedure and just renamed the "temp" file with the output file's name: if
  276.    your current directory wasn't the same directory as the input files, the
  277.    output file wouldn't be in the right directory; use Turbo Pascal FSPLIT
  278.    procedure to get the path to the output file if we need a work file so it
  279.    will be in the right directory *)
  280.  
  281.    if FilCnt = 3 then FNames[3] := FEXPAND(FNames[3])
  282.     else
  283.    BEGIN
  284.       if FilCnt = 1 then FSPLIT(FNames[1], path, name, ext)
  285.        else FSPLIT(FNames[2], path, name, ext);
  286.       FNames[3] := path + '#temp_m#.$$$'
  287.    END;
  288.  
  289.    assign(MergeOut, FNames[3]);
  290.    if (FilCnt = 3) and FileExists(MergeOut) then OutExists(FNames[3]);
  291.  
  292.    reset(List1In);
  293.    if FilCnt > 1 then reset(List2In);
  294.    rewrite(MergeOut)
  295. end; { procedure GetParam }
  296.  
  297. procedure DoMerge;
  298.  
  299. var FileRec : SearchRec;
  300.  
  301. procedure DoLine1;
  302. begin
  303.    if Line1C <> LastLine then
  304.    begin
  305.       LastLine := Line1C;
  306.       writeln(MergeOut, Line1)
  307.    end
  308.     else if not NoDup then writeln(MergeOut, Line1);
  309.  
  310.    readln(List1In, Line1);
  311.    Line1C := Line1;
  312.    if not CaseSen then Line1C := Upper(Line1C)
  313. end; { DoLine1 }
  314.  
  315. procedure DoLine2;
  316. begin
  317.    if Line2C <> LastLine then
  318.    begin
  319.       LastLine := Line2C;
  320.       writeln(MergeOut, Line2)
  321.    end
  322.     else if not NoDup then writeln(MergeOut, Line2);
  323.  
  324.    readln(List2In, Line2);
  325.    Line2C := Line2;
  326.    if not CaseSen then Line2C := Upper(Line2C)
  327. end; { DoLine2 }
  328.  
  329. procedure DoLine12;
  330. begin
  331.    if Line1C <> LastLine then
  332.    begin
  333.       LastLine := Line1C;
  334.       writeln(MergeOut, Line1);
  335.       if not NoDup then writeln(MergeOut, Line2)
  336.    end
  337.     else if not NoDup then
  338.    begin
  339.       writeln(MergeOut, Line1);
  340.       writeln(MergeOut, Line2)
  341.    end;
  342.  
  343.    readln(List1In, Line1);
  344.    readln(List2In, Line2);
  345.    Line1C := Line1;
  346.    Line2C := Line2;
  347.  
  348.    if not CaseSen then
  349.    begin
  350.       Line1C := Upper(Line1C);
  351.       Line2C := Upper(Line2C)
  352.    end
  353. end; { DoLine12 }
  354.  
  355. begin { procedure DoMerge }
  356.    GetParam;
  357.    readln(List1In, Line1);
  358.    Line1C := Line1;
  359.  
  360.    if FilCnt = 1 then Line2C := ''
  361.     else
  362.    begin
  363.       readln(List2In, Line2);
  364.       Line2C := Line2
  365.    end;
  366.  
  367.    if not CaseSen then
  368.    begin
  369.       Line1C := Upper(Line1C);
  370.       Line2C := Upper(Line2C)
  371.    end;
  372.  
  373. (* One more comment to remind myself why I'm reading/writing until the line is
  374.    empty instead of EOF:  EOF becomes true when the last line is read, and
  375.    since I'm doing a priming read, if I did the "while" until EOF, I would
  376.    lose the last line of the input file; it would not get the chance to
  377.    "writeln". *)
  378.  
  379.    LastLine := '';
  380.    while (Line1C <> '') and (Line2C <> '') do
  381.       if Line1C = Line2C then DoLine12
  382.        else if Ascend then
  383.          if Line1C < Line2C then DoLine1
  384.           else DoLine2
  385.        else if Line1C > Line2C then DoLine1
  386.        else DoLine2;
  387.  
  388.    while Line1C <> '' do
  389.       DoLine1;
  390.  
  391.    while Line2C <> '' do
  392.       DoLine2;
  393.  
  394.    close(List1In);
  395.    if FilCnt > 1 then close(List2In);
  396.    close(MergeOut);
  397.  
  398. (* I added this so an input file wouldn't be deleted if an error caused the
  399.    output file's size to be zero; use Turbo Pascal FINDFIRST procedure to get
  400.    the file size ("FileRec" is type "SearchRec" defined in the DOS unit    *)
  401.  
  402.    FINDFIRST(FNames[3], 0, FileRec);
  403.    if FileRec.Size = 0 then erase(MergeOut)
  404.     else if FilCnt = 1 then
  405.    begin
  406.       erase(List1In);
  407.       rename(MergeOut, FNames[1])
  408.    end
  409.     else if FilCnt = 2 then
  410.    begin
  411.       erase(List2In);
  412.       rename(MergeOut, FNames[2])
  413.    end;
  414.  
  415.    close(ScrnOut);
  416.    HALT(0)
  417. end; { procedure DoMerge }
  418.  
  419. begin { Merge }
  420.    ErCode := 0;
  421.    assign(ScrnOut, '');
  422.    rewrite(ScrnOut);
  423.    if paramcount < MinPar then Help else DoMerge
  424. end. { Merge }
  425.