416 строки
7.6 KiB
Plaintext
416 строки
7.6 KiB
Plaintext
% Copyright (C) 2012-2017,2018 John E. Davis
|
|
%
|
|
% This file is part of the S-Lang Library and may be distributed under the
|
|
% terms of the GNU General Public License. See the file COPYING for
|
|
% more information.
|
|
%---------------------------------------------------------------------------
|
|
private variable Pager_Rows = NULL;
|
|
private variable Pager = getenv ("PAGER");
|
|
if (Pager == NULL)
|
|
Pager = "more";
|
|
|
|
% Print Methods
|
|
private variable Print_Device_Type = struct
|
|
{
|
|
fp,
|
|
printf,
|
|
puts,
|
|
close,
|
|
clientdata
|
|
};
|
|
|
|
% Print to file-pointer method
|
|
private define fp_puts_method (p, str)
|
|
{
|
|
variable n = fputs (str, p.fp);
|
|
if (n != strbytelen (str))
|
|
return -1;
|
|
return n;
|
|
}
|
|
|
|
private define fp_printf_method ()
|
|
{
|
|
variable args = __pop_args (_NARGS-1);
|
|
variable p = ();
|
|
return fp_puts_method (p, sprintf (__push_args(args)));
|
|
}
|
|
private define fp_close_method (p)
|
|
{
|
|
return fclose (p.fp);
|
|
}
|
|
private define new_fp_print (fp)
|
|
{
|
|
variable p = @Print_Device_Type;
|
|
p.fp = fp;
|
|
p.puts = &fp_puts_method;
|
|
p.printf = &fp_printf_method;
|
|
return p;
|
|
}
|
|
|
|
% Print to a pager
|
|
|
|
#ifexists SIGPIPE
|
|
private variable Sigpipe_Handler;
|
|
#endif
|
|
|
|
private define close_pager (fp)
|
|
{
|
|
if (fp != NULL)
|
|
() = pclose (fp);
|
|
#ifexists SIGPIPE
|
|
signal (SIGPIPE, Sigpipe_Handler);
|
|
#endif
|
|
}
|
|
private define pager_close_method (p)
|
|
{
|
|
close_pager (p.fp);
|
|
return 0;
|
|
}
|
|
|
|
private define new_pager_print (cmd)
|
|
{
|
|
#ifnexists popen
|
|
return NULL;
|
|
#else
|
|
# ifexists SIGPIPE
|
|
signal (SIGPIPE, SIG_IGN, &Sigpipe_Handler);
|
|
# endif
|
|
variable fp = popen (cmd, "w");
|
|
|
|
try
|
|
{
|
|
if (fp == NULL)
|
|
throw OpenError, "Unable to open the pager ($cmd)"$;
|
|
|
|
# ifexists setvbuf
|
|
() = setvbuf (fp, _IONBF, 0);
|
|
# endif
|
|
variable p = new_fp_print (fp);
|
|
p.close = &pager_close_method;
|
|
return p;
|
|
}
|
|
catch AnyError:
|
|
{
|
|
close_pager (fp);
|
|
throw;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
% Print to a filename
|
|
private define new_file_print (filename)
|
|
{
|
|
variable fp = fopen (filename, "w");
|
|
if (fp == NULL)
|
|
throw OpenError, "Unable to open $filename for writing."$;
|
|
|
|
variable p = new_fp_print (fp);
|
|
p.close = &fp_close_method;
|
|
p.clientdata = filename;
|
|
return p;
|
|
}
|
|
|
|
% Print to a reference
|
|
private define ref_printf_method ()
|
|
{
|
|
variable args = __pop_args (_NARGS-1);
|
|
variable p = ();
|
|
p.fp = strcat (p.fp, sprintf (__push_args(args)));
|
|
return 1;
|
|
}
|
|
private define ref_puts_method (p, str)
|
|
{
|
|
p.fp = strcat (p.fp, str);
|
|
return 1;
|
|
}
|
|
private define ref_close_method (p)
|
|
{
|
|
@p.clientdata = p.fp;
|
|
return 0;
|
|
}
|
|
private define new_ref_print (ref)
|
|
{
|
|
variable p = @Print_Device_Type;
|
|
p.fp = "";
|
|
p.printf = &ref_printf_method;
|
|
p.puts = &ref_puts_method;
|
|
p.close = &ref_close_method;
|
|
p.clientdata = ref;
|
|
return p;
|
|
}
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
private define generic_to_string (x)
|
|
{
|
|
switch (typeof (x))
|
|
{
|
|
case String_Type:
|
|
return make_printable_string (x);
|
|
}
|
|
{
|
|
case BString_Type:
|
|
return sprintf ("\"%S\"", x);
|
|
}
|
|
|
|
return string (x);
|
|
}
|
|
|
|
private define struct_to_string (s, single_line)
|
|
{
|
|
if (s == NULL)
|
|
return "NULL";
|
|
|
|
variable names = get_struct_field_names (s);
|
|
variable comma = "";
|
|
variable str = "{";
|
|
variable comma_str = ", ";
|
|
if (single_line == 0)
|
|
comma_str = ",\n ";
|
|
foreach (names)
|
|
{
|
|
variable name = ();
|
|
str = strcat (str, comma, name, "=", generic_to_string(get_struct_field (s, name)));
|
|
comma = comma_str;
|
|
}
|
|
return strcat (str, "}");
|
|
}
|
|
|
|
private define struct_to_single_line_string (s)
|
|
{
|
|
return struct_to_string (s, 1);
|
|
}
|
|
|
|
private define print_list (a, device)
|
|
{
|
|
if (-1 != device.puts ("{\n"))
|
|
{
|
|
variable s;
|
|
foreach s (a)
|
|
{
|
|
if (-1 == device.printf ("%s\n", generic_to_string (s)))
|
|
break;
|
|
}
|
|
then
|
|
() = device.puts ("}\n");
|
|
}
|
|
}
|
|
|
|
private define write_2d_array (device, a, to_str)
|
|
{
|
|
variable dims = array_shape (a);
|
|
variable nrows = dims[0];
|
|
variable ncols = dims[1];
|
|
|
|
_for (0, nrows-1, 1)
|
|
{
|
|
variable i = ();
|
|
_for (0, ncols-1, 1)
|
|
{
|
|
variable j = ();
|
|
if (-1 == device.printf ("%s ", (@to_str)(a[i,j])))
|
|
return -1;
|
|
}
|
|
if (-1 == device.puts ("\n"))
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private define print_array (a, device)
|
|
{
|
|
variable dims, ndims;
|
|
|
|
(dims, ndims, ) = array_info (a);
|
|
variable nrows = dims[0];
|
|
|
|
try
|
|
{
|
|
variable i, j;
|
|
variable to_str;
|
|
if (_is_struct_type (a))
|
|
to_str = &struct_to_single_line_string;
|
|
else if (__is_numeric (a))
|
|
to_str = &string;
|
|
else
|
|
to_str = &generic_to_string;
|
|
|
|
if (ndims == 1)
|
|
{
|
|
_for i (0, nrows-1, 1)
|
|
{
|
|
if (-1 == device.printf ("%s\n", (@to_str)(a[i])))
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ndims == 2)
|
|
{
|
|
() = write_2d_array (device, a, to_str);
|
|
return;
|
|
}
|
|
|
|
nrows = nint(prod(dims[[0:ndims-3]]));
|
|
variable new_dims = [nrows, dims[ndims-2], dims[ndims-1]];
|
|
reshape (a, new_dims);
|
|
_for i (0, nrows-1, 1)
|
|
{
|
|
if ((-1 == write_2d_array (device, a[i,*,*], to_str))
|
|
|| (-1 == device.puts ("\n")))
|
|
return;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
reshape (a, dims);
|
|
}
|
|
}
|
|
|
|
private define get_pager_rows ()
|
|
{
|
|
if (Pager_Rows != NULL)
|
|
return Pager_Rows;
|
|
|
|
variable rows;
|
|
#ifexists slsh_get_screen_size
|
|
(rows,) = slsh_get_screen_size ();
|
|
#else
|
|
rows = 24;
|
|
#endif
|
|
return rows - 2; % leave room for the prompt
|
|
}
|
|
|
|
|
|
define print ()
|
|
{
|
|
variable usage_string
|
|
= ("print (OBJ [,&str|File_Type|Filename]);\n"
|
|
+ "Qualifiers: pager[=pgm], nopager\n");
|
|
|
|
if (_NARGS == 0)
|
|
usage (usage_string);
|
|
|
|
variable pager_pgm = Pager;
|
|
variable use_pager = -1; % auto
|
|
|
|
if (qualifier_exists("nopager"))
|
|
use_pager = 0;
|
|
else if (qualifier_exists ("pager"))
|
|
{
|
|
use_pager = 1;
|
|
pager_pgm = qualifier ("pager");
|
|
if (pager_pgm == NULL)
|
|
pager_pgm = Pager;
|
|
}
|
|
variable noescape = qualifier_exists ("noescape");
|
|
|
|
variable device = NULL;
|
|
if (_NARGS == 2)
|
|
{
|
|
device = ();
|
|
switch (typeof (device))
|
|
{
|
|
case File_Type:
|
|
device = new_fp_print (device);
|
|
}
|
|
{
|
|
case String_Type:
|
|
device = new_file_print (device);
|
|
}
|
|
{
|
|
case Ref_Type:
|
|
device = new_ref_print (device);
|
|
}
|
|
{
|
|
usage (usage_string);
|
|
}
|
|
use_pager = 0;
|
|
}
|
|
|
|
variable x = ();
|
|
variable t = typeof (x);
|
|
variable str_x = NULL;
|
|
|
|
if (use_pager == -1)
|
|
{
|
|
variable pager_rows = get_pager_rows ();
|
|
|
|
switch (t)
|
|
{
|
|
case Array_Type:
|
|
variable dims = array_shape (x);
|
|
use_pager = ((dims[0] > pager_rows)
|
|
|| (prod(dims) > 10*pager_rows));
|
|
}
|
|
{
|
|
case List_Type:
|
|
use_pager = length (x) > pager_rows;
|
|
}
|
|
{
|
|
case String_Type:
|
|
use_pager = count_byte_occurrences (x, '\n') > pager_rows;
|
|
if (noescape)
|
|
str_x = x;
|
|
}
|
|
{
|
|
if (is_struct_type (x))
|
|
str_x = struct_to_string (x, 0);
|
|
else
|
|
str_x = generic_to_string (x);
|
|
|
|
use_pager = (count_byte_occurrences (str_x, '\n') > pager_rows);
|
|
}
|
|
}
|
|
|
|
if (use_pager)
|
|
device = new_pager_print (pager_pgm);
|
|
|
|
if (device == NULL)
|
|
device = new_fp_print (stdout);
|
|
|
|
try
|
|
{
|
|
if (t == Array_Type)
|
|
return print_array (x, device);
|
|
|
|
if (t == List_Type)
|
|
return print_list (x, device);
|
|
|
|
if ((t == String_Type) && use_pager)
|
|
{
|
|
() = device.puts (x);
|
|
return;
|
|
}
|
|
|
|
if (str_x != NULL)
|
|
x = str_x;
|
|
else if (is_struct_type (x))
|
|
x = struct_to_string (x, 0);
|
|
else
|
|
x = generic_to_string (x);
|
|
|
|
if (-1 != device.puts (x))
|
|
{
|
|
if (x[-1] != '\n')
|
|
() = device.puts ("\n");
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (device.close != NULL)
|
|
() = device.close ();
|
|
}
|
|
}
|
|
|
|
define print_set_pager (pager)
|
|
{
|
|
Pager = pager;
|
|
}
|
|
|
|
define print_set_pager_lines (n)
|
|
{
|
|
Pager_Rows = n;
|
|
}
|
|
|