home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / MISC.ZIP / BEGINEND.PAS next >
Encoding:
Pascal/Delphi Source File  |  1986-01-28  |  18.2 KB  |  726 lines

  1. {
  2. BEGIN/END Pair Checker for Turbo Pascal.
  3. Type BEGINEND ? for an explanation.
  4. written 1/26/86, Kim Kokkonen, TurboPower Software.
  5. Telephone 408-378-3672. Compuserve 72457,2131.
  6. released to the public domain.
  7. requires Turbo Pascal version 3 to compile.
  8. For MSDOS version only. Minor modifications for CP/M.
  9. compile with default options. Heap/Stack requirements are minimal.
  10. }
  11.  
  12. {$V-}
  13. {$I-}
  14. {$C-}
  15. {$G128,P512}
  16.  
  17. PROGRAM BeginEnd(Output);
  18.  {-writes a listing to check for mismatched BEGIN/END pairs}
  19.  
  20. CONST
  21.  version:STRING[5]='1.00';
  22.  optiondelim='-';{character used to introduce a command line option}
  23.  maxnumwrd=128;{max number of words per line}
  24.  maxwrdchr=128;{max number of chars in a word}
  25.  maxnest=40;{max nesting level of BEGINs}
  26.  nr=9;{number of key words}
  27.  endchar:ARRAY[1..4] OF Char=
  28.  ('''','}','*',#00);{end characters for comments and literals}
  29.  
  30. TYPE
  31.  regpack=RECORD
  32.           CASE Integer OF
  33.            1:(ax,bx,cx,dx,bp,si,di,ds,es,flags:Integer);
  34.            2:(al,ah,bl,bh,cl,ch,dl,dh:Byte);
  35.          END;
  36.  word=STRING[maxwrdchr];
  37.  keywords=ARRAY[1..nr] OF word;
  38.  lineword=ARRAY[0..maxnumwrd] OF word;
  39.  lineflag=ARRAY[0..maxnumwrd] OF Boolean;
  40.  lineval=ARRAY[1..maxnumwrd] OF Integer;
  41.  linebuf=STRING[255];
  42.  filestring=STRING[64];
  43.  Textfile=Text[1024];
  44.  
  45. VAR
  46.  reg:regpack;
  47.  tstart,tstop:Real;
  48.  incname,infile:filestring;
  49.  err,inf,incf:Textfile;
  50.  lw:lineword;
  51.  litflag:lineflag;
  52.  li:linebuf;
  53.  
  54.  nestlev,wct,litnum,lcount:Integer;
  55.  
  56.  savenest:ARRAY[0..maxnest] OF Integer;
  57.  declflag:ARRAY[0..maxnest] OF Boolean;
  58.  procname:ARRAY[0..maxnest] OF word;
  59.  
  60.  incfile,including,startofproc,verbose,getprocname,showlines,
  61.  consoleout,endofproc,commflag:Boolean;
  62.  
  63. CONST
  64.  keys:keywords=(
  65.   'BEGIN','END','PROCEDURE','FUNCTION','FORWARD',
  66.   'EXTERNAL','PROGRAM','END.','CASE'
  67.   );
  68.  
  69.  FUNCTION stupcase(s:word):word;
  70.   {-return uppercase value of string}
  71.  VAR
  72.   i:Integer;
  73.  BEGIN
  74.   FOR i:=1 TO Length(s) DO s[i]:=UpCase(s[i]);
  75.   stupcase:=s;
  76.  END;{stupcase}
  77.  
  78.  FUNCTION iostat(bit:Integer):Boolean;
  79.   {-check status of the standard I/O}
  80.   {bit=0 for input, 1 for output}
  81.   {returns true if I/O is through console}
  82.  VAR
  83.   temp0,temp1:Boolean;
  84.  BEGIN
  85.   reg.ax:=$4400;
  86.   reg.bx:=bit;{standard input or output}
  87.   MsDos(reg);
  88.   temp0:=(reg.dx AND 128)<>0;
  89.   temp1:=(reg.dx AND (1 SHL bit))<>0;
  90.   iostat:=temp0 AND temp1;
  91.  END;{iostat}
  92.  
  93.  FUNCTION breakpressed:Boolean;
  94.   {-true if Break key has been pressed}
  95.   {-note that keypressed function executes int 23 if ^C has been pressed}
  96.  VAR
  97.   c:Char;
  98.   breakdown:Boolean;
  99.  BEGIN
  100.   {check current state}
  101.   breakdown:=False;
  102.   WHILE KeyPressed AND NOT(breakdown) DO BEGIN
  103.    Read(Kbd,c);
  104.    IF c=^C THEN breakdown:=True;
  105.   END;
  106.   breakpressed:=breakdown;
  107.  END;{breakpressed}
  108.  
  109.  PROCEDURE breakhalt;
  110.   {-executed when break is detected}
  111.   {-exit with return code 1}
  112.  BEGIN
  113.   Halt(1);
  114.  END;{breakhalt}
  115.  
  116.  PROCEDURE setbreak;
  117.   {-set the ctrl-break address to a process exit handler}
  118.  BEGIN
  119.   reg.ax:=$2523;
  120.   reg.ds:=CSeg;
  121.   reg.dx:=Ofs(breakhalt);
  122.   MsDos(reg);
  123.  END;{setbreak}
  124.  
  125.  PROCEDURE time(VAR sec:Real);
  126.   {-return time of day in seconds since midnight}
  127.  BEGIN
  128.   reg.ah:=$2C;
  129.   MsDos(reg);
  130.   sec:=1.0*(reg.dh+60.0*(reg.cl+60.0*reg.ch)+reg.dl/100.0);
  131.  END;{time}
  132.  
  133.  PROCEDURE defaultextension(extension:filestring;VAR infile:filestring);
  134.   {-assign a default extension to a DOS 2.0+ pathname}
  135.   {extension should be a maximum of 3 characters, and does not include dot}
  136.  VAR
  137.   i:Integer;
  138.   temp:filestring;
  139.  BEGIN
  140.   i:=Pos('..',infile);
  141.   IF i=0 THEN
  142.    temp:=infile
  143.   ELSE
  144.    {a pathname starting with ..}
  145.    temp:=Copy(infile,i+2,64);
  146.   i:=Pos('.',temp);
  147.   IF i=0 THEN infile:=infile+'.'+extension;
  148.  END;{defaultextension}
  149.  
  150.  
  151.  PROCEDURE scanfile(VAR inf:Textfile);
  152.   {-main routine to read file and do the work}
  153.  VAR
  154.   w:word;
  155.   i:Integer;
  156.   localline:Integer;
  157.  
  158.   PROCEDURE parseline(VAR wct:Integer);
  159.    {-strip leading blanks and parse line into "words"}
  160.   VAR
  161.    startpos,lpos,len:Integer;
  162.    getincname,lit,oldword:Boolean;
  163.    cnext,c:Char;
  164.  
  165.    PROCEDURE checkinclude;
  166.     {-see if an include file is requested}
  167.    VAR
  168.     c1,c2,c3:Char;
  169.    BEGIN
  170.     c1:=li[Succ(lpos)];c2:=UpCase(li[lpos+2]);c3:=li[lpos+3];
  171.     IF (c1='$') AND (c2='I') AND NOT((c3='-') OR (c3='+')) THEN BEGIN
  172.      incfile:=True;
  173.      getincname:=True;
  174.     END;
  175.    END;{checkinclude}
  176.  
  177.    PROCEDURE startinc;
  178.     {-get the filename for the include file and finish up the current line}
  179.    VAR
  180.     ch:Char;
  181.     gotstart:Boolean;
  182.  
  183.     PROCEDURE stripblanks(VAR s:filestring);
  184.      {-remove leading and trailing blanks from s}
  185.     VAR
  186.      l,t,len:Integer;
  187.     BEGIN
  188.      l:=0;
  189.      len:=Length(s);
  190.      REPEAT
  191.       l:=Succ(l);
  192.      UNTIL (l>len) OR (s[l]<>' ');
  193.      t:=Succ(len);
  194.      REPEAT
  195.       t:=Pred(t);
  196.      UNTIL (t<1) OR (s[t]<>' ');
  197.      s:=Copy(s,l,Succ(t-l));
  198.     END;{stripblanks}
  199.  
  200.    BEGIN
  201.     IF including THEN BEGIN
  202.      WriteLn;
  203.      WriteLn('cannot nest include files...');
  204.      Halt;
  205.     END;
  206.     incname:='';
  207.     lpos:=lpos+2;{skip $i}
  208.     gotstart:=False;
  209.     REPEAT
  210.      lpos:=Succ(lpos);
  211.      IF li[lpos]<>' ' THEN gotstart:=True;
  212.      incname:=incname+li[lpos];
  213.      ch:=li[Succ(lpos)];
  214.     UNTIL (ch='*') OR (ch='}') OR (gotstart AND (ch=' '));
  215.     stripblanks(incname);
  216.     defaultextension('PAS',incname);
  217.     getincname:=False;
  218.    END;{startinc}
  219.  
  220.    PROCEDURE saveword;
  221.     {-store the parsed word into the word array}
  222.    BEGIN
  223.     lw[wct]:=Copy(li,startpos,lpos-startpos);
  224.     wct:=Succ(wct);
  225.     litflag[wct]:=commflag;
  226.     oldword:=False;
  227.     startpos:=lpos;
  228.    END;{saveword}
  229.  
  230.    PROCEDURE singdelim;
  231.     {-make a one char delimiter word}
  232.    BEGIN
  233.     IF oldword THEN saveword;
  234.     lw[wct]:=c;
  235.     wct:=Succ(wct);
  236.     litflag[wct]:=commflag;
  237.     oldword:=False;
  238.     startpos:=Succ(lpos);
  239.    END;{singdelim}
  240.  
  241.    PROCEDURE doubdelim;
  242.     {-make a two character delimiter word}
  243.    BEGIN
  244.     IF oldword THEN saveword;
  245.     lw[wct]:=c+cnext;
  246.     wct:=Succ(wct);
  247.     litflag[wct]:=commflag;
  248.     oldword:=False;
  249.     lpos:=Succ(lpos);{move over an extra character}
  250.     startpos:=Succ(lpos);
  251.    END;{doubdelim}
  252.  
  253.    PROCEDURE dopart;
  254.     {-interpret partial delimiters}
  255.    BEGIN
  256.     IF lpos<len THEN BEGIN
  257.      cnext:=li[Succ(lpos)];
  258.      IF (cnext='=') OR (cnext='>') THEN doubdelim ELSE singdelim;
  259.     END ELSE singdelim;
  260.    END;{dopart}
  261.  
  262.    PROCEDURE startlit;
  263.     {-start a new comment or constant string}
  264.  
  265.     PROCEDURE setlit;
  266.      {-initialize the flags}
  267.     BEGIN
  268.      lit:=True;
  269.      oldword:=True;
  270.      litflag[wct]:=True;
  271.     END;{setlit}
  272.  
  273.    BEGIN
  274.     IF oldword THEN saveword;
  275.     IF c='''' THEN BEGIN
  276.      litnum:=1;
  277.      setlit;
  278.     END ELSE IF c='{' THEN BEGIN
  279.      litnum:=2;
  280.      setlit;
  281.      commflag:=True;
  282.      IF (lpos+2)<len THEN checkinclude;
  283.     END ELSE IF c='!' THEN BEGIN
  284.      litnum:=4;
  285.      setlit;
  286.     END ELSE IF lpos<len THEN BEGIN
  287.      IF ((c='(') AND (li[Succ(lpos)]='*')) THEN BEGIN
  288.       litnum:=3;
  289.       setlit;
  290.       commflag:=True;
  291.       lpos:=Succ(lpos);
  292.       IF (lpos+2)<len THEN checkinclude;
  293.      END ELSE singdelim;
  294.     END ELSE singdelim;
  295.    END;{startlit}
  296.  
  297.    PROCEDURE endlit;
  298.     {-see if the comment or literal string has been terminated}
  299.    BEGIN
  300.     IF c='}' THEN BEGIN
  301.      commflag:=False;
  302.      lit:=False;
  303.     END ELSE IF c='*' THEN BEGIN
  304.      IF ((lpos<len) AND (li[Succ(lpos)]=')')) THEN BEGIN
  305.       commflag:=False;
  306.       lit:=False;
  307.       lpos:=Succ(lpos);{move past the ')'}
  308.      END;{else keep the literal going}
  309.     END ELSE IF (lpos=len) OR (li[Succ(lpos)]<>'''') THEN
  310.      lit:=False
  311.     ELSE
  312.      {keep the literal going, skip the next ' character}
  313.      lpos:=Succ(lpos);
  314.    END;{endlit}
  315.  
  316.   BEGIN{parseline}
  317.    wct:=0;len:=Length(li);
  318.    IF len>0 THEN BEGIN
  319.  
  320.     lpos:=0;
  321.     REPEAT{skip leading blanks}
  322.      lpos:=Succ(lpos);
  323.      IF lpos>len THEN Exit;
  324.     UNTIL (li[lpos]<>' ');
  325.  
  326.     lit:=commflag;{initialize line variables}
  327.     wct:=1;
  328.     litflag[1]:=commflag;
  329.     oldword:=commflag;
  330.     startpos:=lpos;
  331.     getincname:=False;
  332.  
  333.     REPEAT{scan through the line}
  334.      c:=li[lpos];
  335.      IF lit THEN BEGIN
  336.       {we only care to find the end marker of the literal}
  337.       IF c=endchar[litnum] THEN endlit;
  338.       lpos:=Succ(lpos);
  339.       IF NOT(lit) THEN saveword;
  340.      END ELSE BEGIN{literal flag not on}
  341.       IF (c<'A') OR (c>'z') THEN BEGIN
  342.        {delimiters and numbers}
  343.        IF (c='!') OR (c='''') OR (c='{') OR (c='(') THEN BEGIN
  344.         {comment delimiters}
  345.         startlit;
  346.         IF getincname THEN BEGIN
  347.          startinc;
  348.         END;
  349.        END ELSE BEGIN
  350.         IF (c<'0') OR (c>'9') THEN BEGIN
  351.          {non-comment delimiters}
  352.          IF (c=' ') OR (c=^I) THEN BEGIN
  353.           IF oldword THEN saveword;
  354.           startpos:=Succ(lpos);
  355.          END ELSE IF (c=':') OR (c='<') OR (c='>') THEN
  356.           dopart
  357.          ELSE IF (c=')') OR (c=';') OR (c=',') OR (c='=') OR
  358.          (c='+') OR (c='-') OR (c='*') OR (c='/') THEN singdelim
  359.          ELSE oldword:=True;
  360.         END ELSE oldword:=True;
  361.        END;
  362.       END ELSE BEGIN
  363.        {normal characters except for [,],^}
  364.        IF (c='[') OR (c=']') THEN singdelim
  365.        ELSE oldword:=True;
  366.       END;
  367.       lpos:=Succ(lpos);
  368.      END;
  369.  
  370.     UNTIL lpos>len;
  371.     IF oldword THEN saveword;
  372.     wct:=Pred(wct);
  373.    END;
  374.   END;{parseline}
  375.  
  376.   FUNCTION keyword(VAR w:word):Boolean;
  377.    {-see if a word is a keyword and capitalize it}
  378.   VAR
  379.    j:Integer;
  380.   BEGIN
  381.    w:=stupcase(w);
  382.    keyword:=False;
  383.    FOR j:=1 TO nr DO
  384.     IF w=keys[j] THEN BEGIN
  385.      keyword:=True;
  386.      Exit;
  387.     END;
  388.   END;{keyword}
  389.  
  390.   PROCEDURE anakey(VAR w:word);
  391.    {-analyze a keyword for effect on indentation, etc}
  392.  
  393.    PROCEDURE check(nestlev:Integer);
  394.     {-make sure nestlev falls into legal bounds}
  395.    BEGIN
  396.     IF nestlev>maxnest THEN BEGIN
  397.      WriteLn(err);
  398.      WriteLn(err,'Exceeded program nesting capacity...');
  399.      Halt(2);
  400.     END;
  401.     IF nestlev<0 THEN BEGIN
  402.      WriteLn(err);
  403.      WriteLn(err,'Too many END statements...');
  404.      Halt(2);
  405.     END;
  406.    END;{check}
  407.  
  408.   BEGIN
  409.    IF (w='BEGIN') THEN BEGIN
  410.     IF declflag[nestlev] THEN BEGIN
  411.      startofproc:=True;
  412.      savenest[nestlev]:=nestlev;
  413.     END ELSE BEGIN
  414.      nestlev:=Succ(nestlev);
  415.      savenest[nestlev]:=-1;
  416.     END;
  417.     declflag[nestlev]:=False;
  418.    END ELSE IF w='END' THEN BEGIN
  419.     IF NOT(declflag[nestlev]) THEN BEGIN
  420.      IF nestlev=savenest[nestlev] THEN
  421.       endofproc:=True;
  422.      nestlev:=Pred(nestlev);
  423.     END;
  424.    END ELSE IF w='CASE' THEN BEGIN
  425.     IF NOT(declflag[nestlev]) THEN BEGIN
  426.      nestlev:=Succ(nestlev);
  427.      declflag[nestlev]:=False;
  428.      savenest[nestlev]:=-1;
  429.     END;
  430.    END ELSE IF (w='PROCEDURE') OR (w='FUNCTION') THEN BEGIN
  431.     nestlev:=Succ(nestlev);
  432.     getprocname:=True;
  433.     declflag[nestlev]:=True;
  434.    END ELSE IF (w='FORWARD') OR (w='EXTERNAL') THEN BEGIN
  435.     {ignore forward and external declaration lines}
  436.     nestlev:=Pred(nestlev);
  437.    END ELSE IF w='PROGRAM' THEN BEGIN
  438.     getprocname:=True;
  439.    END ELSE IF w='END.' THEN BEGIN
  440.     endofproc:=True;
  441.     nestlev:=Pred(nestlev);
  442.    END ELSE
  443.     Exit;
  444.    check(nestlev);
  445.   END;{anakey}
  446.  
  447.   PROCEDURE increment(VAR lcount,localline:Integer);
  448.    {-increment line counter and check for wraparound}
  449.   BEGIN
  450.    lcount:=Succ(lcount);
  451.    IF lcount<0 THEN lcount:=0;
  452.    localline:=Succ(localline);
  453.   END;{increment}
  454.  
  455.   PROCEDURE writeoutline(lcount,localline:Integer;li:linebuf);
  456.    {-format the output line}
  457.   VAR
  458.    l,n:linebuf;
  459.   BEGIN
  460.    IF showlines THEN BEGIN
  461.     Str(lcount:5,n);
  462.     l:='{'+n;
  463.     IF verbose THEN BEGIN
  464.      l:=l+' (';
  465.      IF including THEN
  466.       l:=l+stupcase(incname)
  467.      ELSE
  468.       l:=l+stupcase(infile);
  469.      Str(localline:5,n);
  470.      l:=l+' '+n+')';
  471.     END;
  472.     l:=l+'} '+li;
  473.    END ELSE
  474.     l:=li;
  475.    IF consoleout THEN
  476.     WriteLn(Copy(l,1,79))
  477.    ELSE
  478.     WriteLn(l);
  479.   END;{writeoutline}
  480.  
  481.   PROCEDURE writemarker(p:word;m:Char);
  482.    {-write a marker at begin or end of a procedure}
  483.   VAR
  484.    l:linebuf;
  485.   BEGIN
  486.    FillChar(l[1],78,m);
  487.    l[0]:=Chr(79);
  488.    l[1]:='{';
  489.    Move(p[1],l[2],Length(p));
  490.    l[79]:='}';
  491.    WriteLn(l);
  492.   END;{writemarker}
  493.  
  494.   PROCEDURE doincludeit;
  495.    {-includes include files}
  496.   BEGIN
  497.    Assign(incf,incname);
  498.    Reset(incf);
  499.    IF IOResult<>0 THEN BEGIN
  500.     WriteLn(err);WriteLn(err);
  501.     WriteLn(err,'include file ',incname,' not found');
  502.     Close(inf);
  503.     Halt(2);
  504.    END;
  505.    incfile:=False;
  506.    including:=True;
  507.    scanfile(incf);
  508.    including:=False;
  509.   END;{doincludeit}
  510.  
  511.  BEGIN
  512.   localline:=0;
  513.   REPEAT
  514.  
  515.    {read the input file, line by line}
  516.    ReadLn(inf,li);
  517.  
  518.    {status checking and display}
  519.    IF breakpressed THEN breakhalt;
  520.    increment(lcount,localline);
  521.    IF NOT(consoleout) THEN
  522.     IF (lcount AND 15=0) THEN
  523.      Write(err,^M,lcount);
  524.  
  525.    {break the line into words to be analyzed}
  526.    parseline(wct);
  527.  
  528.    i:=1;
  529.    WHILE i<=wct DO BEGIN
  530.     {pass through line and interpret words}
  531.     IF NOT(litflag[i]) THEN BEGIN
  532.      w:=lw[i];
  533.      IF getprocname THEN BEGIN
  534.       procname[nestlev]:=w;
  535.       getprocname:=False;
  536.      END;
  537.      IF keyword(w) THEN anakey(w);
  538.     END;
  539.     i:=Succ(i);
  540.    END;{of word scan}
  541.  
  542.    {write out the line and any pre or post lines}
  543.    IF startofproc THEN BEGIN
  544.     writemarker(stupcase(procname[nestlev]),'+');
  545.     startofproc:=False;
  546.    END;
  547.  
  548.    {here is the user line}
  549.    writeoutline(lcount,localline,li);
  550.  
  551.    IF endofproc THEN BEGIN
  552.     writemarker(stupcase(procname[Succ(nestlev)]),'-');
  553.     endofproc:=False;
  554.    END;
  555.  
  556.    IF IOResult<>0 THEN BEGIN
  557.     WriteLn(err);
  558.     WriteLn(err,'error during write....');
  559.     Halt(2);
  560.    END;
  561.  
  562.    {include the include file if found}
  563.    IF incfile THEN doincludeit;
  564.  
  565.   UNTIL EoF(inf);
  566.   Close(inf);
  567.  END;{scanfile}
  568.  
  569.  PROCEDURE setval;
  570.   {-interpret command line and prepare for run}
  571.  VAR
  572.   j:Integer;
  573.   haltsoon:Boolean;
  574.   arg:linebuf;
  575.  
  576.   PROCEDURE writehelp;
  577.    {-write a help screen}
  578.   BEGIN
  579.    WriteLn(err,'Usage:  BEGINEND [MainSourceFile] [>OutPutFile]');
  580.    WriteLn(err);
  581.    WriteLn(err,'  BEGINEND writes a formatted listing of the program to the standard');
  582.    WriteLn(err,'  output device. The listing includes the global line number in front');
  583.    WriteLn(err,'  of each source line, and optionally the include file name and the');
  584.    WriteLn(err,'  line number within the include file.');
  585.    WriteLn(err);
  586.    WriteLn(err,'  More importantly, the BEGIN and END statements that mark entry and');
  587.    WriteLn(err,'  exit from procedures and functions are marked with a dividing line.');
  588.    WriteLn(err,'  The dividing line is composed of +++ characters on entry and ---');
  589.    WriteLn(err,'  characters on exit. Each dividing line is also labeled with the name of');
  590.    WriteLn(err,'  the associated procedure. Mismatched BEGIN/END pairs will become');
  591.    WriteLn(err,'  obvious when you see that the marker lines are out of whack.');
  592.    WriteLn(err);
  593.    WriteLn(err,'Options:');
  594.    WriteLn(err,'  -V  Verbose mode. Writes include file name and local line number');
  595.    WriteLn(err,'  -N  No line numbers are written before any line.');
  596.    WriteLn(err,'  -?  Writes this help message.');
  597.    Halt(0);
  598.   END;{writehelp}
  599.  
  600.  BEGIN
  601.   infile:='';
  602.   haltsoon:=False;
  603.   {scan the argument list}
  604.   j:=1;
  605.   WHILE j<=ParamCount DO BEGIN
  606.    arg:=ParamStr(j);
  607.    IF (arg='?') OR (arg=optiondelim+'?') THEN
  608.     writehelp
  609.    ELSE IF (arg[1]=optiondelim) AND (Length(arg)=2) THEN
  610.     CASE UpCase(arg[2]) OF
  611.      'V':verbose:=True;
  612.      'N':showlines:=False;
  613.     ELSE
  614.      WriteLn(err,'unrecognized command option');
  615.      haltsoon:=True;
  616.     END
  617.    ELSE BEGIN
  618.     {input file name}
  619.     infile:=arg;
  620.     {add default extension}
  621.     defaultextension('PAS',infile);
  622.     {make sure it exists}
  623.     Assign(inf,infile);
  624.     Reset(inf);
  625.     IF IOResult<>0 THEN BEGIN
  626.      WriteLn(err,stupcase(infile),' not found or marked READ-ONLY....');
  627.      haltsoon:=True;
  628.     END;
  629.    END;
  630.    j:=Succ(j);
  631.   END;
  632.   IF infile='' THEN BEGIN
  633.    WriteLn(err,'no input file specified....');
  634.    Halt;
  635.   END ELSE IF haltsoon THEN BEGIN
  636.    WriteLn(err,'type BEGEND ? for help');
  637.    Halt;
  638.   END;
  639.  END;{setval}
  640.  
  641.  PROCEDURE getfiles;
  642.   {-get filenames,open up and assure good files}
  643.  VAR
  644.   good:Boolean;
  645.  
  646.   FUNCTION getyesno(m:linebuf;default:Char):Boolean;
  647.    {-return true for yes, false for no}
  648.   VAR
  649.    ch:Char;
  650.   BEGIN
  651.    WriteLn(err);
  652.    Write(err,m);
  653.    REPEAT
  654.     Read(Kbd,ch);
  655.     IF ch=^C THEN BEGIN
  656.      WriteLn('^C');
  657.      Halt;
  658.     END;
  659.     IF ch=#13 THEN ch:=default ELSE ch:=UpCase(ch);
  660.    UNTIL (ch IN ['Y','N']);
  661.    WriteLn(err,ch);
  662.    getyesno:=(ch='Y');
  663.   END;{getyesno}
  664.  
  665.  BEGIN
  666.   REPEAT
  667.    Write(err,'Enter input file name (<cr> to quit): ');
  668.    ReadLn(infile);
  669.    IF Length(infile)=0 THEN Halt;
  670.    {add default extension}
  671.    defaultextension('PAS',infile);
  672.    Assign(inf,infile);
  673.    Reset(inf);
  674.    IF IOResult<>0 THEN BEGIN
  675.     WriteLn(err,stupcase(infile),' not found or marked READ-ONLY. try again....');
  676.     good:=False;
  677.    END ELSE
  678.     good:=True;
  679.   UNTIL good;
  680.   showlines:=
  681.   getyesno('Show global line numbers preceding each line? (<cr> for Yes) ','Y');
  682.   IF showlines THEN
  683.    verbose:=
  684.    getyesno('Show Include file names and local line numbers? (<cr> for No) ','N')
  685.   ELSE
  686.    verbose:=False;
  687.   WriteLn(err);
  688.  END;{getfiles}
  689.  
  690.  PROCEDURE initglobals;
  691.  BEGIN
  692.   setbreak;
  693.   lcount:=0;nestlev:=1;
  694.   commflag:=False;incfile:=False;getprocname:=False;
  695.   startofproc:=False;endofproc:=False;showlines:=True;
  696.   including:=False;incfile:=False;verbose:=False;
  697.   declflag[1]:=True;
  698.   procname[1]:='PROGRAM';
  699.   consoleout:=iostat(1);
  700.   Assign(err,'ERR:');
  701.   Rewrite(err);
  702.   WriteLn(err);
  703.   WriteLn(err,'Pascal BEGIN/END Checker - by TurboPower Software - Version ',version);
  704.   WriteLn(err);
  705.  END;{initglobals}
  706.  
  707.  PROCEDURE showrate;
  708.  VAR
  709.   deltat:Real;
  710.  BEGIN
  711.   Write(err,^M'total lines processed: ',lcount);
  712.   deltat:=tstop-tstart;
  713.   IF deltat>0 THEN BEGIN
  714.    WriteLn(err,'  rate: ',(lcount/deltat):6:1,' LPS');
  715.   END ELSE WriteLn(err);
  716.  END;{showrate}
  717.  
  718. BEGIN{petmod}
  719.  initglobals;
  720.  IF ParamCount=0 THEN getfiles ELSE setval;
  721.  time(tstart);
  722.  scanfile(inf);
  723.  time(tstop);
  724.  showrate;
  725. END.
  726.