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

     $RCSfile: Args.mod $
  Description: C-style command-line argument parsing

   Created by: fjc (Frank Copeland)
    $Revision: 2.3 $
      $Author: fjc $
        $Date: 1994/08/08 16:10:27 $

  Copyright © 1994, Frank Copeland.
  This file is part of the Oberon-A Library.
  See Oberon-A.doc for conditions of use and distribution.

***************************************************************************)

MODULE Args;

(*
** $C- CaseChk       $I- IndexChk  $L- LongAdr   $N- NilChk
** $P- PortableCode  $R- RangeChk  $S= StackChk  $T- TypeChk
** $V- OvflChk       $Z- ZeroVars
*)

IMPORT E := Exec, D := Dos, WB := Workbench, SYS := SYSTEM;

TYPE
  ArgVPtr *= POINTER TO ARRAY OF E.STRPTR;

VAR

  IsCLI *     : BOOLEAN;
    (*
     * TRUE = program started from CLI, FALSE = program started from
     * Workbench
     *)

  argc *      : LONGINT;
    (* Number of arguments passed by CLI *)

  argv *      : ArgVPtr;
    (*
     * Array of argument strings passed by CLI.
     * argv [0] is always the name of the program.
     *)

  NumArgs *   : LONGINT;
    (* Number of arguments passed by Workbench *)

  ArgList *   : WB.WBArgumentsPtr;
    (* Array of WBArg structures passed by Workbench *)

  DosCmdLen * : LONGINT;
    (* Length of the command string passed by the CLI *)

  DosArgs *   : E.STRPTR;
    (* The actual command string passed by the CLI; !! DO NOT CHANGE !! *)

  argCopy     : E.STRPTR;
    (* Copy of the command string passed by the CLI *)

  startDir    : D.FileLockPtr;

(*------------------------------------*)
PROCEDURE GetArgs ();

  VAR argLen : LONGINT; args : E.APTR;

  (*------------------------------------*)
  PROCEDURE CliArgs ();

    VAR index, index2 : LONGINT; nameLen : INTEGER;
        process : D.ProcessPtr; prCLI : D.CommandLineInterfacePtr;

  BEGIN (* CliArgs *)
    IsCLI := TRUE; NumArgs := 0; ArgList := NIL;
    argCopy := NIL; argc := 1; (* First arg is always program name *)
    DosCmdLen := argLen; DosArgs := args;

    IF DosCmdLen > 0 THEN
      (* Make a copy of the command string *)
      SYS.NEW (argCopy, SYS.STRLEN (DosArgs^) + 1);
      COPY (DosArgs^, argCopy^);

      (*
       * Scan the copy, planting a NUL at the end of each argument and
       * keeping a count of the arguments found;
       *)
      index := 0;
      LOOP
        IF index >= DosCmdLen THEN (* last argument found *) EXIT END;
        (* Kill any leading spaces *)
        WHILE argCopy [index] = " " DO
          argCopy [index] := 0X; INC (index)
        END;
        IF argCopy [index] = 22X THEN (* a quoted argument *)
          (* zap the quote *)
          argCopy [index] := 0X; INC (index);
          (* scan for the next quote *)
          WHILE argCopy [index] # 22X DO INC (index) END;
          (* zap it too *)
          argCopy [index] := 0X; INC (index);
          INC (argc)
        ELSIF argCopy [index] > " " THEN (* an unquoted argument *)
          (* scan for the end of the argument *)
          WHILE argCopy [index] > " " DO INC (index) END;
          (* mark it *)
          argCopy [index] := 0X; INC (index);
          INC (argc)
        ELSE
          (*
           * This is probably a dummy "\n" at the end of an empty command
           * line.
           *)
          argCopy [index] := 0X; INC (index)
        END; (* IF *)
      END; (* LOOP *)

      (* Allocate the argv array *)
      NEW (argv, argc + 1);

      IF argc > 0 THEN (* Fill argv with pointers to arguments *)
        index := 0; index2 := 1;
        WHILE index2 < argc DO
          WHILE argCopy [index] = 0X DO INC (index) END;
          argv [index2] := SYS.ADR (argCopy [index]);
          INC (index2);
          WHILE argCopy [index] # 0X DO INC (index) END
        END; (* WHILE *)
      END; (* IF *)

      (* Terminate it with a NIL *)
      argv [argc] := NIL
    ELSE
      (* Create a dummy entry for argv *)
      NEW (argv, 2);
      argv [1] := NIL
    END; (* ELSE *)

    (* Get the command name *)
    process := SYS.VAL (D.ProcessPtr, E.base.FindTask (NIL));
    nameLen := ORD (process.cli.commandName [0]);
    SYS.NEW (argv [0], nameLen + 1, {E.memClear});
    SYS.MOVE (SYS.ADR (process.cli.commandName [1]), argv [0], nameLen);
    (* argv [0, nameLen] := 0X; *)

    startDir := NIL
  END CliArgs;

  (*------------------------------------*)
  PROCEDURE WBArgs ();

    VAR wbMsg : WB.WBStartupPtr;

  BEGIN (* WBArgs *)
    IsCLI := FALSE; argc := 0; argv := NIL;
    DosCmdLen := 0; DosArgs := NIL; argCopy := NIL;
    wbMsg := args;
    NumArgs := wbMsg.numArgs; ArgList := wbMsg.argList;
    IF NumArgs = 1 THEN
      startDir := D.base.CurrentDir (ArgList [0].lock)
    ELSE
      startDir := D.base.CurrentDir (ArgList [1].lock)
    END
  END WBArgs;

BEGIN (* GetArgs *)
  SYS.ARGLEN (argLen); SYS.ARGS (SYS.VAL (LONGINT, args));
  IF argLen >= 0 THEN CliArgs ()
  ELSE WBArgs ()
  END
END GetArgs;

(*------------------------------------*)
PROCEDURE* Cleanup ();

BEGIN (* Cleanup *)
  IF startDir # NIL THEN startDir := D.base.CurrentDir (startDir) END
END Cleanup;

BEGIN (* Args *)
  GetArgs ();
  SYS.SETCLEANUP (Cleanup)
END Args.
