(*(***********************************************************************

:Program.    Autodoc.mod
:Contents.   extracts autodocs from source
:Author.     Hartmut Goebel [hG]
:Address.    Aufseßplatz 5, D-8500 Nürnberg 40
:Address.    UseNet: hartmut@oberon.nbg.sub.org
:Address.    Z-Netz: hartmut@asn.zer   Fido: 2:246/81.1
:Copyright.  Copyright © 1993 by Hartmut Goebel
:Language.   Oberon
:Translator. Amiga Oberon V3.0
:History.    V2.0, 03 Jan 1993 [hG]

:Version.    $VER: Autodoc.mod 2.6 (10.1.93)

(* $StackChk- $NilChk- $RangeChk- $CaseChk- $OvflChk- $ReturnChk- $ClearVars- *)
***********************************************************************)*)

MODULE Autodoc;

IMPORT
  args:= Arguments,
  Break,  (* checks for Ctrl-C *)
  Conversions,
  asc := ASCII,
  avl := AVL,
  fs := FileSystem,
  io,
  ms := MoreStrings,
  str := Strings;

CONST
  versionString = "$VER: autodoc 2.6 (10.1.93)";

TYPE
  Entry = POINTER TO EntryDesc;
  EntryDesc = RECORD (avl.SNode)
    start: LONGINT;
    interface: BOOLEAN;
  END;

VAR
  nArgs, argCnt: INTEGER;
  file: fs.File;
  root: avl.SRoot;
  filename, text: ARRAY 256 OF CHAR;

CONST
  defaultTextWidth = 78; maxTextWidth = SIZE(text);
  defaultTabSize = 8;

VAR
  internal: BOOLEAN;
  toc, ff, wordWrap: BOOLEAN;
  cStyle, asteric, modula, semicolon: BOOLEAN;
  textWidth, tabSize: LONGINT;

PROCEDURE TableOfContents(node: avl.NodePtr);
BEGIN
  WITH node: Entry DO
    io.WriteString(node.name);
    io.WriteLn;
  END;
END TableOfContents;


PROCEDURE WriteEntry(node: avl.NodePtr);

  PROCEDURE WrapString();
  VAR
    c: CHAR;
  BEGIN
    IF wordWrap THEN
      LOOP
        IF str.Length(text) < textWidth THEN EXIT END;
        c := text[textWidth];
        text[textWidth] := CHR(0);
        io.WriteString(text); io.WriteLn;
        text[textWidth] := c;
        str.Delete(text,0,textWidth);
      END;
    END;
    io.WriteString(text); io.WriteLn;
  END WrapString;

VAR
  i: INTEGER;
  eFile: fs.File;
BEGIN
  WITH node: Entry DO
    io.WriteString(node.name);
    (* $RangeChk- *)
    i := SHORT(textWidth) - 2 * SHORT(str.Length(node.name)); (* name is max 80 char *)
    (* $RangeChk= *)
    WHILE i > 0 DO
     io.Write(" "); DEC(i);
    END;
    io.WriteString(node.name);
    io.WriteLn;
    IF node.interface THEN
      IF ~ fs.Open(eFile,filename,FALSE) THEN
        io.WriteString("can't open .def"); io.WriteLn;
        HALT(20);
      END;
      io.WriteLn;
      WHILE fs.ReadString(eFile,text) DO
        WrapString;
      END;
      IF ff THEN io.Write(asc.ff); END;
      IF fs.Close(eFile) THEN END;
    ELSE
      IF ~ fs.Move(file,node.start) THEN
        io.WriteString("error in file");
        io.WriteLn;
        HALT(20);
      END;
      WHILE fs.ReadString(file,text) DO
        str.Delete(text,0,1);
        IF ms.StrCmpN(text,"***",3) = 0 THEN
          IF ff THEN io.Write(asc.ff); END;
          RETURN;
        END;
        WrapString;
      END;
    END;
  END;
END WriteEntry;


PROCEDURE ProcessFile();
VAR
  entry: Entry;
  i: LONGINT;
  adtext: ARRAY 10 OF CHAR;

  PROCEDURE IsEntryStart(): BOOLEAN;
  CONST
    adTextLen  = 5; intPos = 5;
    astText  = "*****";
    modText  = "(****";
    semText  = ";****";
    cStText  = "/****";

  BEGIN
    RETURN (  internal & (text[intPos] = "i") OR
            ~ internal & (text[intPos] = "*"))
         & (text[intPos+1] = "*") & (text[intPos+2] = " ")
         & (asteric   & (ms.StrCmpN(astText,text,adTextLen) = 0) OR
            modula    & (ms.StrCmpN(modText,text,adTextLen) = 0) OR
            semicolon & (ms.StrCmpN(semText,text,adTextLen) = 0) OR
            cStyle    & (ms.StrCmpN(cStText,text,adTextLen) = 0));
  END IsEntryStart;

BEGIN
  IF ~ fs.Open(file,filename,FALSE) THEN
    io.WriteString("can't open file "); io.WriteString(filename); io.WriteLn;
    HALT(20);
  END;
  avl.SInit(root);

  (* check for .def *)
  IF modula THEN
    i := SHORT(str.Occurs(filename,".mod"));
    IF (i > 0) & (i = str.Length(filename)-4) THEN
      filename[i] := CHR(0);
      text := filename;
      str.Append(filename,".def");
      IF fs.Exists(filename) THEN
        NEW(entry);
        i := ms.OccursCharPos(text,"/",-SHORT(str.Length(text)-1));
        IF i >= 0 THEN
          str.Delete(text,0,i+1);
        END;
        str.Append(text,"/--interface--");
        COPY(text,entry.name);
        entry.interface := TRUE;
        IF avl.SAdd(root,entry) THEN END;
      END;
    END;
  END;

  WHILE fs.ReadString(file,text) DO
    IF IsEntryStart() THEN
      str.Delete(text,0,8);
      i := ms.OccursChar(text," ");
      IF i >= 0 THEN
        text[i] := CHR(0);
      END;
      NEW(entry);
      COPY(text,entry.name);
      entry.start := fs.Position(file);
      entry.interface := FALSE;
      IF ~ avl.SAdd(root,entry) THEN
        io.WriteString("double entry");
        io.WriteLn;
        HALT(20);
      END;
    END;
  END;
  IF file.status # fs.eof THEN
    io.WriteString("error reading file");
    io.WriteLn;
    HALT(20);
  END;
  IF toc THEN
    io.WriteString("TABLE OF CONTENTS");
    io.WriteLn; io.WriteLn;
    avl.DoForward(root,TableOfContents);
    IF ff THEN io.Write(asc.ff); END;
  END;
  avl.DoForward(root,WriteEntry);
 IF fs.Close(file) THEN END;
END ProcessFile;


BEGIN
  filename := versionString;

  nArgs := args.NumArgs();
  IF (nArgs = 0) THEN
    io.WriteString("Autodoc 2.0 by hartmut Goebel"); io.WriteLn;
    io.WriteString("Usage: autodoc [options] infile [infile]"); io.WriteLn;
    io.WriteLn;
    io.WriteString("Options"); io.WriteLn;
    io.WriteString("-i     - Process only INTERNAL autodocs"); io.WriteLn;
    io.WriteString("-a     - Process autodocs starting with an asteric"); io.WriteLn;
    io.WriteString("-s     - Process autodocs starting with a semicolon"); io.WriteLn;
    io.WriteString("-m     - Process autodocs starting with (*"); io.WriteLn;
    io.WriteString("-C     - Process autodocs starting with /*"); io.WriteLn;
    io.WriteString("-w     - Turn off word wrap"); io.WriteLn;
    io.WriteString("-f     - No form feeds between entries"); io.WriteLn;
    io.WriteString("-I     - Output Table of Contents before entries"); io.WriteLn;
    io.WriteString("-l<num>- Set line length to <num> (default 78, max. 256)"); io.WriteLn;
    HALT(20);
  END;

  internal := FALSE; cStyle := FALSE; asteric := FALSE; modula := FALSE;
  semicolon := FALSE;
  toc := FALSE; ff := TRUE; wordWrap := TRUE;
  textWidth := defaultTextWidth-1; tabSize := defaultTabSize;

  argCnt := 0;
  REPEAT
    INC(argCnt);
    args.GetArg(argCnt,filename);
    IF filename[0] # "-" THEN
      ProcessFile();
    ELSE
      CASE filename[1] OF
      "i": internal := TRUE; |
      "a": asteric := TRUE; |
      "s": semicolon := TRUE; |
      "m": modula := TRUE; |
      "C": cStyle := TRUE; |
      "w": wordWrap := FALSE; |
      "f": ff := FALSE; |
      "I": toc := TRUE; |
      "l": str.Delete(filename,0,2);
           IF Conversions.StringToInt(filename,textWidth) THEN
             DEC(textWidth);
             IF textWidth > maxTextWidth THEN
               textWidth := maxTextWidth;
             ELSIF textWidth < 0 THEN
               textWidth := 0;
             END;
           ELSE
             textWidth := defaultTextWidth;
           END;
      ELSE
      END;
    END;
  UNTIL argCnt = nArgs;
END Autodoc.
