{ utils.a68 - Utility routines for godcc.

  Copyright (C) 2026 Jose E. Marchesi

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program 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 General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see
  <https://www.gnu.org/licenses/>. }

module Utils =
    access Globals
def
    pub proc isdigit = (char c) bool:
           ABS c >= ABS "0" AND ABS c <= ABS "9";

    pub proc ishexdigit = (char c) bool:
           ((ABS c >= ABS "0" AND ABS c <= ABS "9")
            OR (ABS c >= ABS "a" AND ABS c <= ABS "f")
            OR (ABS c >= ABS "A" AND ABS c <= ABS "F"));

    pub proc atoi = (string s, int base) int:
        begin
              proc htoi = (char c) int:
                 ABS (c >= "0" AND c <= "9" | REPR (ABS c - ABS "0")
                      |: c >= "a" AND c <= "f" | REPR (ABS c - ABS "a" + 10)
                      |: c >= "A" AND c <= "F" | REPR (ABS c - ABS "A" + 10));

              proc valid = (char c) bool:
                 (base <= 10 | isdigit(c) | ishexdigit(c));

              int num := 0;
              for i to UPB s while valid(s[i])
              do num := num * base + htoi(s[i])
              od;
              num
        end;

    pub proc itoa = (int i, int base) string:
           if i = 0
           then "0"
           else int n := ABS i;
                string res;
                while n /= 0
                do int rem = n %* base;
                   res := REPR (rem > 9
                                | (rem - 10) + ABS "a"
                                | rem + ABS "0") + res;
                   n %:= base
                od;
                (i < 0 | "-" + res | res)
           fi;

    pub proc char_in_string = (string s, char c) int:
        begin int res := 0, bool found := false;
              for i to UPB s while NOT found
              do (s[i] = c | res := i; found := true) od;
              res
        end;

    proc errorno = (string msg) void:
       (error(msg + ": " + strerror (errno)));

    pub proc fopen_checked = (string path, bits flags) int:
           (int res = fopen(path, flags);
            (res /= -1 | res | errorno("opening " + path); skip));

    pub proc fcreate_checked = (string path, bits mode_) int:
           (int res = fcreate(path, mode_);
            (res /= -1 | res | errorno("creating " + path); skip));

    pub proc fclose_checked = (string path, int fd) int:
           (int res = fclose(fd);
            (res /= -1 | res | errorno("closing " + path); skip));

    pub proc fputs_checked = (string path, int fd, string str) int:
           (int res = fputs(fd, str);
            (res = 0 AND UPB str > 0 | errorno("writing to " + path); skip | res));

    pub proc fsize_checked = (string path, int fd) long long int:
           (long long int res = fsize(fd);
            (res = - long long 1 | errorno("getting size of file " + path); skip | res));

    { Read the contents of the file at the given PATH and return them
      as a string.

      If PATH is - then read contents from the standard input.  Error
      out if the file cannot be found.

      Note how this routine returns a name rather than a string.  This
      is because the contents of the file can be rather large.  The
      string is allocated on the heap.  The returned reference allows
      the caller to modify the string. }

    pub proc read_from_file = (string path) ref string:
    begin int fd = (path = "-" | 0 | fopen_checked(path, file_o_rdonly));
          long long int size = fsize_checked(path, fd);
          (size > LENG LENG max_int | error("input file is too big"));
          fgets(fd, SHORTEN SHORTEN fsize_checked(path, fd))
    end;

    { Create an output file in PATH and write the contents of the
      given string CONTENTS to it.

      If PATH is - then write to the standard output.  Error out if
      the file cannot be created or written to. }

    pub proc write_to_file = (string path, string contents) void:
    begin int fd = (path = "-" | 1 | fcreate_checked(path, 8r644));
          fputs_checked (path, fd, contents);
          (fd /= 1 | fclose_checked(path, fd))
    end;

    skip
fed
