1
1
s-lang/slsh/lib/slshrl.sl

447 строки
9.2 KiB
Plaintext
Исходник Постоянная ссылка Обычный вид История

% -*- mode: SLang; mode: fold -*-
% This file was written with the help of Mike Noble and John Houck.
require ("slshhelp");
if (is_defined ("slsh_help"))
use_namespace ("slsh_interactive");
else
implements ("slsh_interactive");
#ifnexists quit
public define quit () { exit (0); }
#endif
public define slsh_apropos ()
{
if (_NARGS == 0)
{
vmessage ("apropos what?");
return;
}
variable name = ();
variable help = _apropos ("Global", "\\C"+name, 0xF);
variable ns = current_namespace ();
if (strlen (ns)
and (ns != "Global"))
{
variable help1 = _apropos (ns, "\\C"+name, 0xF);
help1 = (ns + "->") + help1;
help = [help, help1];
}
if (0 == length (help))
{
() = fprintf (stdout, "No matches for %S\n", name);
return;
}
() = fprintf (stdout, " apropos %s ==>\n", name);
help = help[array_sort(help)];
foreach (help)
{
help = ();
() = fprintf (stdout, " %s\n", help);
}
}
private define generic_help ()
{
variable help =
[
"Most commands must end in a semi-colon.",
"If a command begins with '!', then the command is passed to the shell.",
" Examples: !ls, !pwd, !cd foo, ...",
"Parenthesis are automatically added if the first word is a function and",
"is followed by a ','. For example:",
" plot, 1, 2;color=\"red\" ==> plot(1,2;color=\"red\");",
"Special commands:",
" help <help-topic>",
" apropos <something>",
" start_log( <optional-log-file> );",
" start logging input to a file (default is slsh.log)",
" stop_log();",
" stop logging input",
" save_input (<optional-file>);",
" save all previous input to a file (default: slsh.log)",
" who;",
" show a list of locally defined variables and functions",
" quit;"
];
foreach (help)
{
variable h = ();
() = fprintf (stdout, "%s\n", h);
}
}
public define slsh_help ()
{
if (_NARGS == 0)
{
generic_help ();
return;
}
variable name = ();
variable help = slsh_get_doc_string (name);
if (help != NULL)
{
print (help; noescape);
return;
}
() = fprintf (stdout, "*** No help on %s\n", name);
slsh_apropos (name);
}
public define slsh_who ()
{
variable a, name, obj;
variable pat;
if (_NARGS == 0)
"";
pat = ();
variable names = _apropos ("", pat, 8); % user defined variable
foreach (names[array_sort (names)]) % user defined variable
{
name = ();
variable type;
obj = __get_reference (name);
if (__is_initialized (obj))
{
obj = @obj;
type = typeof (obj);
}
else
{
type = Undefined_Type;
obj = "";
}
if ((type == String_Type) || (type == BString_Type))
print (obj, &obj);
variable txt = sprintf ("%S %S=%S", type, name, obj);
if (strlen(txt) > 79)
txt = substr (txt, 1, 76) + "...";
message (txt);
}
names = _apropos ("", pat, 2); % user defined function
foreach (names[array_sort(names)])
{
name = ();
vmessage ("function %s()", name);
}
}
private variable Append_Semicolon = 0;
public define slsh_append_semicolon (val)
{
Append_Semicolon = val;
}
static define sys_shell_cmd (cmd)
{
variable status;
status = system (cmd);
!if (status)
return;
() = fprintf (stdout, "shell command returned %d\n", status);
}
static define sys_chdir_cmd (dir) %{{{
{
if (-1 == chdir (dir))
() = fprintf (stderr, "chdir(%s) failed: %s\n", dir, errno_string (errno));
}
%}}}
% Support interactive mode which doesn't require the ';' EOL mark.
% Note that this no-semicolon-required mode causes surprising
% behavior in the '.source' intrinsic. I'm taking the view
% that that's the price users must pay for the convenience of
% not typing the semicolons.
private define maybe_append_semicolon (input)
{
!if (Append_Semicolon)
return input;
if (input[-1] != ';')
input = strcat (input, ";");
return input;
}
% This function is called after input has been read.
% It should be rewritten and made cleaner.
public define slsh_interactive_massage_hook (input)
{
variable s, n;
variable s0;
variable ch;
input = strtrim (input);
!if (strlen (input))
return input;
ch = input[0];
if (ch == '!')
{
% shell command, unlesss !if
if (string_match (input, "^!if[ \t]*(", 1))
return input;
input = strtrim (input[[1:]]);
input = str_quote_string (input, "\"", '\\');
% cd is special. On unix systems we do not want to execute it in
% a shell.
if (strtok (input)[0] == "cd")
{
return sprintf ("slsh_interactive->sys_chdir_cmd(\"%s\");",
strtrim (input[[2:]]));
}
return sprintf ("slsh_interactive->sys_shell_cmd(\"%s\");", input);
}
if (ch != '.')
{
% If the first non-word character is a comma, and the first
% word is callable, then wrap the rest of the arguments in
% parenthesis.
s = strtrim (strtrans (input, "\\w", ""));
if (s[0] == ',')
{
s0 = strtok (input, ", \t")[0];
if (__is_callable(__get_reference(s0)))
{
(s,) = strreplace (input, ",", "(", 1);
return strcat (s, ");");
}
}
s = strtok (input, " \t");
s = strtok (input, " \t()\";");
if (length (s) == 0)
return input;
s0 = s[0];
if ((s0 == "exit") && (length (s)==1))
usage ("Try 'quit' to exit");
if (all (s0 != ["help", "apropos", "quit", "who"]))
return maybe_append_semicolon (input);
}
else % line begins with "."
{
if (input[1] == ' ')
{
% RPN
return input;
}
% The only thing that is syntactically valid here that begins
% with a '.' is a floating point number.
if (isdigit(input[1]))
{
% Do not allow the line to be parsed as RPN. So prefix
% with a space.
return strcat (" ", maybe_append_semicolon (input));
}
%return sprintf ("eval(\" %s\");", input);
input = input[[1:]];
s = strtok (input);
s0 = s[0];
}
if (length (s) < 2)
{
if (s0 == "help")
return "slsh_help();";
if (s0 == "quit")
return "exit(0);";
if (s0 == "who")
return "slsh_who();";
return input;
}
if (length (s) == 2)
{
% Here the input consists of 2 words such as:
% cd foo
% load bar
% help goo
% apropos boo
variable s1 = s[1];
if (s1[0] != '(')
{
if ((s0 == "apropos") or (s0 == "help"))
{
s0 = strcat ("slsh_", s0);
if (0 == is_substr (s1, "\""))
return sprintf ("%s(\"%s\");", s0, s1);
else
return sprintf ("%s(%s);", s0, s1);
}
if (s0 == "cd")
return sprintf ("slsh_interactive->sys_chdir_cmd(\"%s\");", s1);
% Assume the next arg is a slang script.
if ((strlen(path_extname (s1)) == 0)
and (NULL != stat_file (s1 + ".sl")))
s1 = s1 + ".sl";
if (s0 == "load")
return sprintf ("()=evalfile(\"%s\");", s1);
#iffalse
if (s0 == "source")
return sprintf ("%s(\"%s\");", s0, s1);
#endif
}
}
% Anything else that does not look like a slang command will be converted
% to a function call
input = sprintf ("%s(%s);", s0, input[[strlen (s0):]]);
return input;
}
%}}}
%---------------------------------------------------------------------------
% Logging Functions
%---------------------------------------------------------------------------
private variable Log_File = "slsh.log";
public define slsh_set_log_file (logfile)
{
Log_File = logfile;
}
private variable Log_File_Fp = NULL;
private variable Input_Line_List = NULL;
private variable Last_Input_Line = NULL;
private define open_log_file (file)
{
variable fp = fopen (file, "w");
if (fp == NULL)
vmessage ("***Warning: Unable to log to %s\n", file);
return fp;
}
private define log_this ()
{
variable args = __pop_args (_NARGS);
if (Log_File_Fp == NULL)
return;
if (-1 == fprintf (Log_File_Fp, __push_args(args)))
{
vmessage ("Failed to write to log file-- logging stopped\n");
Log_File_Fp = NULL;
}
}
public define start_log ()
{
if (_NARGS)
Log_File = ();
Log_File_Fp = open_log_file (Log_File);
if (Log_File_Fp == NULL)
return;
log_this ("%% Logging started on %s\n", time ());
log_this ("_auto_declare=%d;\n\n", _auto_declare);
vmessage ("Logging input to %s\n", Log_File);
}
public define stop_log ()
{
log_this ("%% Logging stopped on %s\n", time ());
Log_File_Fp = NULL;
}
public define save_input ()
{
variable file;
!if (_NARGS)
Log_File;
file = ();
if (Input_Line_List == NULL)
return;
variable fp = open_log_file (file);
if (fp == NULL)
return;
variable l = Input_Line_List;
while (l != NULL)
{
() = fprintf (fp, "%s\n", l.line);
l = l.next;
}
vmessage ("Input saved to %s", file);
}
private define log_input (buf)
{
buf = strtrim (buf);
!if (strlen (buf))
return;
variable l = struct
{
line, next
};
l.line = buf;
if (Input_Line_List == NULL)
{
Input_Line_List = l;
}
else Last_Input_Line.next = l;
Last_Input_Line = l;
if (Log_File_Fp != NULL)
log_this ("%s\n", buf);
}
public define slsh_interactive_after_hook (line)
{
log_input (line);
}
public define slsh_interactive_before_hook ()
{
variable n = _stkdepth ();
_stk_reverse (n);
loop (n)
{
variable v = ();
() = fprintf (stdout, "%S\n", v);
}
}