/* pk-dump.pk - `dump' command.  */

/* Copyright (C) 2019, 2020, 2021, 2022, 2023, 2024 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 <http://www.gnu.org/licenses/>.
 */

/* The operation of `dump' can be configured by the user by
   customizing the following variables.  */

load ios;

var pk_dump_size = 128#B;
var pk_dump_group_by = 2#B;
var pk_dump_cluster_by = 8;
var pk_dump_ruler = 1;
var pk_dump_ascii = 1;
var pk_dump_nonprintable_char = '.';
var pk_dump_unknown_byte = "??";
var pk_dump_max_line_width = 90;

pk_help_add_topic
  :entry Poke_HelpEntry {
          category = "commands",
          topic = "dump",
          summary = "display the contents of a range in the current IO space",
          description= format ("
Synopsis:

  dump [:from OFFSET] [:size OFFSET] [:val VAL] [:ios INT] [:ruler BOOL] \\
        [:ascii BOOL] [:group_by INT] [:cluster_by INT] [:max_line_width INT]

Arguments:

  :from (offset)
         Beginning of the range to dump. When this argument is not
         specified, `dump' uses the last used offset in the selected
         IO space, which is initially 0#B.

  :size (offset)
         How much data to dump.  Defaults to `pk_dump_size', initially
         %v.

  :val (any)
         A mapped value whose constituent bytes are to be dumped.  If
         a non-negative 32-bit integer, then it is used as an index
         referring to a past result.  If a negative 32-bit integer
         then this argument is ignored.  Note that if :var is used
         then any specified :from and :size are ignored.

         Quick access to the original value and its constituents can
         be achieved by using the `dumpval' function.  See `.help
         dumpval'.

  :ios (int)
         IO space from which dump bytes.  This defaults to the currently
         selected IO space.

  :ruler (int)
         Boolean indicating whether to display a ruler.  Defaults to
         `pk_dump_ruler', initially %i32d.

  :ascii (int)
         Boolean indicating whether to display an ASCII dump at the
         right of the bytes dump.  Defaults to `pk_dump_ascii',
         initially %i32d.

  :group_by (int)
         How are bytes grouped together in the output.  Defaults to
         `pk_dump_group_by', initially %v.

  :cluster_by (int)
         Display additional space after the specified number of groups
         have been displayed.  Defaults to `pk_dump_cluster_by',
         initially %i32d.

  :max_line_width (int)
         Maximum size of lines to use in the byte dump.  If negative
         then there is no limit.  Defaults to `pk_dump_max_line_width',
         initially %i32d.

If there is not a current IO space available `dump' raises an E_no_ios
exception.

See `.doc dump' for more information.",
                               pk_dump_size, pk_dump_ruler, pk_dump_ascii,
                               pk_dump_group_by, pk_dump_cluster_by,
                               pk_dump_max_line_width)
          };

/* `pk_dump_offsets' keeps the last base offset used by `dump', per IO
   space.  These are the offsets to be used in case the command is
   invoked with no :from argument.  */

type Pk_Dump_Offset =
  struct
  {
    int<32> ios;
    offset<int<64>,b> offset;
  };

var pk_dump_offsets = Pk_Dump_Offset[]();

fun pk_dump_get_offset = (int<32> ios) offset<int<64>,b>:
{
  for (e in pk_dump_offsets where e.ios == ios)
    return e.offset;
  return 0#B;
}

fun pk_dump_set_offset = (int<32> ios, offset<int<64>,b> offset) void:
{
  for (e in pk_dump_offsets where e.ios == ios)
    {
      e.offset = offset;
      return;
    }

  pk_dump_offsets += [Pk_Dump_Offset { ios = ios, offset = offset }];
}

/* Accessor to dump :val saved values.  */

pk_help_add_topic
  :entry Poke_HelpEntry {
      category = "poke functions",
      topic = "dumpval",
      summary = "quickly retrieve values shown by dump :val",
      description = "
Synopsis:

  dumpval (int<32> VAL = -1) any

Arguments:

  val (int)
        The index of a value whose bytes have been printed by the last
        `dump :val' command."
    };

fun dumpval = (int<32> idx = -1) any:
{
  var val = ios_dumpval_last[0];

  try
    {
      if (idx < 0)
        {
          if (ios_dumpval_last[0] isa int<32>)
            raise E_out_of_bounds;
          return ios_dumpval_last[0];
        }
      else
        return ios_dumpval[idx];
    }
  catch if E_out_of_bounds
    {
      raise Exception { code = EC_inval,
                        name = "invalid argument",
                        msg = "no such dumpval" };
    }
}

/* And the command itself.  */

fun dump = (int<32> ios = get_ios,
            offset<int<64>,b> from = pk_dump_get_offset (ios),
            offset<int<64>,b> size = pk_dump_size,
            any val = -1,
            offset<int<64>,b> group_by = pk_dump_group_by,
            int<32> cluster_by = pk_dump_cluster_by,
            int<32> ruler = pk_dump_ruler,
            int<32> ascii = pk_dump_ascii,
            int<32> max_line_width = pk_dump_max_line_width) void:
{
  ios_dump_bytes :ios ios :from from :size size :group_by group_by :val val
                 :cluster_by cluster_by :ruler_p ruler :ascii_p ascii
                 :unknown_byte pk_dump_unknown_byte
                 :max_line_width pk_dump_max_line_width;
  pk_dump_set_offset (ios, from);
}
