--
--	Input functionality
--	Copyright (C) 1985 P.H. Welch
--
--	This library is free software; you can redistribute it and/or
--	modify it under the terms of the GNU Lesser General Public
--	License as published by the Free Software Foundation; either
--	version 2 of the License, or (at your option) any later version.
--
--	This library is distributed in the hope that it will be useful,
--	but WITHOUT ANY WARRANTY; without even the implied warranty of
--	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
--	Lesser General Public License for more details.
--
--	You should have received a copy of the GNU Lesser General Public
--	License along with this library; if not, write to the Free Software
--	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
--

--** @module course

#INCLUDE "consts.inc"

--{{{  PROC file.in.string
--* Read a string from a channel non-interactively.
-- Lines are terminated by newline characters.
-- @param s Buffer to read into
-- @param length Number of bytes read
-- @param max Maximum number of bytes to read
-- @param in Channel to read from
PROC file.in.string ([]BYTE s,  INT length,  VAL INT max,  CHAN BYTE in?)
  --{{{  non-interactive input of s using in (maximum of max characters)
  INT cut.max:
  BOOL ok:
  BYTE ch:
  SEQ
    --{{{  initialise
    SEQ
      IF
        max > (SIZE s)
          cut.max := SIZE s
        TRUE
          cut.max := max
      ok := FALSE
      length := 0
      in ? ch
    --}}}
    --{{{  main cycle
    WHILE ch <> '*n'
      SEQ
        --{{{  process ch
        IF
          length = cut.max
            --{{{  
            SKIP
            --}}}
          (' ' <= ch) AND (ch <= '~')
            --{{{  
            SEQ
              s [length] := ch
              length := length + 1
            --}}}
          TRUE
            --{{{  
            SKIP
            --}}}
        --}}}
        in ? ch
    --}}}
    --{{{  finalisation
    SEQ i = length FOR (SIZE s) - length
      s[i] := NULL
    --}}}
  --}}}
:
--}}}
--{{{  PROC file.in.token
--* Read a token from a channel non-interactively.
-- Tokens are terminated by whitespace.
-- @param t Buffer to read into
-- @param length Number of bytes read
-- @param max Maximum number of bytes to read
-- @param in Channel to read from
PROC file.in.token ([]BYTE t,  INT length,  VAL INT max,  CHAN BYTE in?)
  --{{{  non-interactive input of t using in (maximum of max characters)
  INT cut.max:
  BOOL ok:
  BYTE ch:
  SEQ
    --{{{  initialise
    SEQ
      IF
        max > (SIZE t)
          cut.max := SIZE t
        TRUE
          cut.max := max
      ok := FALSE
      length := 0
      in ? ch
    --}}}
    --{{{  find start of token
    WHILE (ch = ' ') OR (ch = '*t') OR (ch = '*c') OR (ch = '*n')
      in ? ch
    --}}}
    --{{{  find end of token
    WHILE NOT ((ch = ' ') OR (ch = '*t') OR (ch = '*c') OR (ch = '*n') OR
               (ch = END.OF.FILE))
      SEQ
        --{{{  process ch
        IF
          length = cut.max
            --{{{  
            SKIP
            --}}}
          (' ' <= ch) AND (ch <= '~')
            --{{{  
            SEQ
              t[length] := ch
              length := length + 1
            --}}}
          TRUE
            --{{{  
            SKIP
            --}}}
        --}}}
        in ? ch
    --}}}
    --{{{  finalisation
    SEQ i = length FOR (SIZE t) - length
      t[i] := NULL
    --}}}
  --}}}
:
--}}}
--{{{  BOOL FUNCTION file.check.num.chars
--* Checks that a string has the format of a legal integer.
-- This is a low-level routine used by [@ref file.in.int].
-- @param s String to check
-- @param length Length of string
-- @return Whether the string is valid
-- @private
BOOL FUNCTION file.check.num.chars (VAL []BYTE s,  VAL INT length)
  --{{{  
  BOOL ok:
  VALOF
    --{{{  is it ok?
    IF
      (('0' > s[0]) OR (s[0] > '9')) AND ((s[0] <> '+') AND (s[0] <> '-'))
        ok := FALSE
      IF i = 1 FOR length - 1
        ('0' > s[i]) OR (s[i] > '9')
          ok := FALSE
      length > 1
        ok := TRUE
      length = 1
        ok := ('0' <= s[0]) AND (s[0] <= '9')
      TRUE
        ok := FALSE
    --}}}
    RESULT ok
  --}}}
:
--}}}
--{{{  INT FUNCTION file.convert.num.chars
--* Parse an integer value stored in decimal.
-- This assumes that [@code s] has the format of a legal integer.  It computes
-- that integer and returns it.  It does [@em not] guard against overflow.
-- This is a low-level routine used by [@ref file.in.int].
-- @param s String to convert
-- @param length Length of string
-- @return Integer value of string
-- @private
INT FUNCTION file.convert.num.chars (VAL []BYTE s,  VAL INT length)
  --{{{  
  INT x:
  VALOF
    --{{{  convert to x
    INT start, n.digits:
    BOOL positive:
    SEQ
      --{{{  check for sign
      IF
        s[0] = '+'
          --{{{  
          SEQ
            positive := TRUE
            start := 1
            n.digits := length - 1
          --}}}
        s[0] = '-'
          --{{{  
          SEQ
            positive := FALSE
            start := 1
            n.digits := length - 1
          --}}}
        TRUE
          --{{{  
          SEQ
            positive := TRUE
            start := 0
            n.digits := length
          --}}}
      --}}}
      --{{{  compute number
      SEQ
        x := 0
        SEQ i = start FOR n.digits
          x := (10*x) + ( (INT s[i]) - (INT '0') )
      --}}}
      --{{{  set the sign
      IF
        positive
          SKIP
        TRUE
          x := -x
      --}}}
    --}}}
    RESULT x
  --}}}
:
--}}}
--{{{  PROC file.in.int
--* Read an integer from a channel non-interactively.
-- @param n Integer read
-- @param max Maximum number of bytes to read
-- @param ok Whether a number was successfully read
-- @param in Channel to read from
PROC file.in.int (INT n,  VAL INT max,  BOOL ok,  CHAN BYTE in?)
  --{{{  non-interactive input of n using in (up to max digits) (sets ok flag)
  VAL INT max.num.digits IS 10:
  [max.num.digits]BYTE s:
  INT length, cut.max:
  SEQ
    --{{{  cut.max := min (max, max.num.digits)
    IF
      max > max.num.digits
        cut.max := max.num.digits
      TRUE
        cut.max := max
    --}}}
    file.in.token (s, length, cut.max, in?)
    #PRAGMA DEFINED s
    ok := file.check.num.chars (s, length)
    IF
      ok
        n := file.convert.num.chars (s, length)
      TRUE
        SKIP
  --}}}
:
--}}}

--{{{  deprecated stuff
--* Synonym for [@ref file.in.int].
-- @deprecated
PROC file.in.number (INT n, VAL INT max, BOOL ok, CHAN BYTE in?)
  file.in.int (n, max, ok, in?)
:
--}}}  

