File: //usr/share/jed/lib/info.sl
%_debug_info = 1;
%
% Info reader for JED
%
variable Info_This_Filename = Null_String;
variable Info_This_Filedir = Null_String;
#ifndef VMS
% returns compression extension if file is compressed or "" if not
define info_is_compressed (file)
{
variable exts, ext, n;
exts = ".Z,.z,.gz,.bz2";
n = 0;
forever
{
ext = extract_element(exts, n, ',');
if (ext == NULL) return "";
if (1 == file_status(file + ext)) break;
n++;
}
ext;
}
#endif
define info_make_file_name (file)
{
variable n, dir, dirfile, df, df_low;
variable cext = ""; % compressed extension
n = 0;
forever
{
%
% Try to find requested file in remembered directory.
%
dirfile = expand_filename(dircat(Info_This_Filedir, file));
if (1 == file_status(dirfile)) break;
dir = extract_element(Info_Directory, n, ',');
if (dir == NULL) dir = "";
df = expand_filename(dircat(dir,file));
% try with first with info extension
#ifdef VMS
dirfile = df + "info"; % VMS adds a '.' upon expansion
#else
dirfile = df + ".info";
#endif
if (1 == file_status(dirfile)) break;
#ifndef VMS
cext = info_is_compressed(dirfile);
if (strlen(cext)) break;
#endif
df_low = expand_filename(dircat(dir,strlow(file)));
#ifdef VMS
dirfile = df_low + "info"; % VMS adds a '.' upon expansion
#else
dirfile = df_low + ".info";
#endif
if (1 == file_status(dirfile)) break;
#ifndef VMS
cext = info_is_compressed(dirfile);
if (strlen(cext)) break;
#endif
% try next with inf extension, since .info causes problems on FAT
% In addition, Unix and VMS distributions may have been derived from
% PC 8+3 distributions.
%
% Also Windows 95 supports long filenames. Since that OS is also
% considered to be MSDOS, we need to try this for MSDOS as well
% even though it has no effect under a true MSDOS system.
dirfile = df_low + ".inf";
if (1 == file_status(dirfile)) break;
#ifndef VMS
cext = info_is_compressed(dirfile);
if (strlen(cext)) break;
#endif
% repeat without extension
dirfile = df;
if (1 == file_status(dirfile)) break;
#ifndef VMS
cext = info_is_compressed(dirfile);
if (strlen(cext)) break;
#endif
dirfile = df_low;
if (1 == file_status(dirfile)) break;
#ifdef UNIX
cext = info_is_compressed(dirfile);
if (strlen(cext)) break;
#endif
ifnot (strlen(dir)) error ("Info file not found: " + file);
++n;
}
(Info_This_Filedir, ) = parse_filename(dirfile);
return (dirfile, cext);
}
private define make_unzip_cmd (ext)
{
variable unzip_cmd = "uncompress -c";
switch (ext)
{
case ".gz":
unzip_cmd = "gzip -dc";
}
{
case ".bz2":
unzip_cmd = "bzip2 -dc";
}
return unzip_cmd;
}
define info_find_file (file)
{
variable dirfile, flags, buf, dir;
variable ext;
(dirfile, ext) = info_make_file_name(file);
setbuf("*Info*");
set_readonly(0);
widen(); erase_buffer();
#ifndef VMS
if (strlen(ext))
() = run_shell_cmd (sprintf("%s %s%s", make_unzip_cmd (ext), dirfile, ext));
else
#endif
() = insert_file(dirfile);
bob();
Info_This_Filename = dirfile;
set_readonly(1);
set_buffer_modified_flag(0);
set_mode("Info", 1);
use_keymap("Infomap");
set_status_line(" Jed Info: %f (%m%n) Press '?' for help. (%p)", 0);
% The following 2 lines will cause problems when the buffer attached to
% the specified file is erased from a re-only directory.
%( , dir, buf, flags) = getbuf_info();
%setbuf_info(extract_filename(Info_This_Filename), dir, buf, flags);
}
define info_find_node_split_file(); % extern
variable Info_Split_File_Buffer; Info_Split_File_Buffer = Null_String;
variable Info_Split_Filename; Info_Split_Filename = Null_String;
define info_search_marker(dir)
{
variable mark, pnt, search_fun;
mark = "\x1F";
if (dir > 0) search_fun = &fsearch; else search_fun = &bsearch;
push_mark();
forever
{
if (not(@search_fun(mark)))
{
pop_mark_1 ();
return(0);
}
if (bolp()) break;
pnt = _get_point ();
bol(); skip_chars("\x01-\x1E ");
go_right_1 ();
pnt = _get_point () - pnt;
if ((pnt == 1) and (eolp() or looking_at_char('\xC'))) break;
if (dir > 0) eol(); else bol();
}
pop_mark_0 ();
return (1);
}
define info_find_node_this_file (the_node)
{
variable node, len, fnd;
CASE_SEARCH = 0;
node = "Node: " + the_node;
len = strlen(node);
widen(); bob();
forever
{
% some of this could/should be replaced by a regular expression:
% ifnot (re_fsearch("^[\t ]*\x1F")) ....
ifnot (info_search_marker(1))
{
% dont give up, maybe this is a split file
ifnot (strlen(Info_Split_File_Buffer))
error("Marker not found. " + node);
setbuf(Info_Split_File_Buffer);
info_find_node_split_file(the_node);
return;
}
go_down_1 (); % bol(); --- already implicit
if (ffind(node))
{
% is this really it? ---
go_right(len);
if (eolp() or looking_at_char(',') or looking_at_char('\t')) break;
}
eol ();
}
push_mark();
if (info_search_marker(1)) go_up_1(); else eob();
narrow();
bob();
}
define info_find_node_split_file (node)
{
variable tag, tagpos, pos, pos_len, tag_len, buf, file;
variable re;
buf = " *Info*";
ifnot (bufferp(buf), setbuf(buf))
{
insbuf("*Info*");
}
widen();
% make this re safe
tag = str_quote_string (node, "\\^$[]*.+?", '\\');
tag = "Node: " + tag;
eob();
%ifnot (bol_bsearch(tag)) error("tag not found.");
%go_right(strlen(tag));
%skip_chars(" \t\x7F");
re = tag + "[\t \x7F]\\d+[ \t]*$";
ifnot (re_bsearch(re)) verror ("tag %s not found.", tag);
eol ();
bskip_chars(" \t");
push_mark(); bskip_chars ("0-9");
tagpos = bufsubstr(); % see comment about DOS below
tag_len = strlen(tagpos);
bob ();
bol_fsearch("Indirect:"); pop();
push_mark();
ifnot (info_search_marker(1)) eob();
narrow();
bob();
forever
{
ifnot (down_1 ()) break;
% bol(); --- implicit in down
ifnot (ffind(": ")) break;
go_right(2);
% This will not work on DOS with 16 bit ints. Do strcmp instead.
push_mark_eol(); pos = bufsubstr();
pos_len = strlen(pos);
if (tag_len > pos_len) continue;
if (tag_len < pos_len) break;
% now ==
if (strcmp(tagpos, pos) < 0) break;
}
Info_Split_File_Buffer = Null_String;
go_up_1 (); bol();
push_mark();
() = ffind(": ");
widen();
file = bufsubstr();
info_find_file(file);
info_find_node_this_file(node);
Info_Split_File_Buffer = buf;
}
define info_narrow()
{
if (whatbuf () != "*Info*") return;
push_spot(); push_spot();
() = info_search_marker(-1);
go_down_1 (); push_mark();
pop_spot();
if (info_search_marker(1)) go_up_1 (); else eob();
narrow();
pop_spot();
}
% stack for last position
ifnot (is_defined ("Info_Position_Type"))
{
typedef struct
{
filename,
split_filename,
line_number
}
Info_Position_Type;
}
variable Info_Position_Stack = Info_Position_Type [16];
variable Info_Stack_Depth = 0;
define info_push_position(file, split, line)
{
variable i;
variable pos;
if (Info_Stack_Depth == 16)
{
--Info_Stack_Depth;
for (i = 1; i < 16; i++)
Info_Position_Stack [i - 1] = @Info_Position_Stack [i];
}
pos = Info_Position_Stack [Info_Stack_Depth];
pos.filename = file;
pos.split_filename = split;
pos.line_number = line;
++Info_Stack_Depth;
}
define info_record_position ()
{
variable i, file;
if (whatbuf() != "*Info*") return;
widen();
file = Null_String;
if (strlen (Info_Split_File_Buffer)) file = Info_Split_Filename;
info_push_position(Info_This_Filename, file, what_line());
info_narrow();
}
define info_find_node(node)
{
variable the_node, file, n, len;
n = 0;
% Replace \n and \t characters in name by spaces
node = strcompress (node, " \t\n");
info_record_position();
ERROR_BLOCK
{
if (bufferp ("*Info*"))
info_reader ();
}
len = strlen(node);
% if it looks like (file)node, extract file, node
if (is_substr(node, "(") == 1) n = is_substr(node, ")");
if (n)
{
variable save_node = "|" + node + "|";
the_node = node;
node = substr(the_node, n + 1, strlen(node));
the_node = strsub(the_node, n, 0); % truncate string
file = substr(the_node, 2, n-2);
if (bufferp(Info_Split_File_Buffer)) delbuf(Info_Split_File_Buffer);
Info_Split_File_Buffer = Null_String;
info_find_file(file);
}
node = strtrim (node);
ifnot (strlen(node)) node = "Top";
widen();
push_spot_bob ();
ifnot (info_search_marker(1)) error("Marker not found.");
go_down_1 ();
if (looking_at("Indirect:"), pop_spot())
{
Info_Split_Filename = Info_This_Filename;
info_find_node_split_file(node);
}
else info_find_node_this_file(node);
}
% If buffer has a menu, point is put on line after menu marker if argument
% is non-zero, otherwise leave point as is.
% signals error if no menu.
define info_find_menu(save)
{
variable menu_re = "^\\c\\* Menu:";
push_spot();
bob ();
forever
{
ifnot (re_fsearch(menu_re))
{
pop_spot();
error ("Node has no menu.");
}
go_right (7);
ifnot (looking_at_char (':'))
break;
}
ifnot (save)
{
pop_spot();
return;
}
eol(); go_right_1 ();
push_mark(); pop_spot(); pop_mark_1 ();
}
% Move move the cursor to the start of the next nearest menu item or
% note reference in this node if possible.
%
define info_next_xref ()
{
push_mark (); go_right_1 ();
if (re_fsearch("\\*.*:")) exchange_point_and_mark ();
pop_mark_1 ();
}
%
% Move move the cursor to the start of the previous nearest menu item or
% note reference in this node if possible.
%
define info_prev_xref ()
{
push_mark (); go_left_1 ();
if (re_bsearch("[*].*:")) exchange_point_and_mark ();
pop_mark_1 ();
}
% menu references
define info_follow_current_xref ()
{
variable node;
push_spot();
ifnot (fsearch_char (':'))
{
pop_spot(); error ("Corrupt File?");
}
if (looking_at("::"))
{
push_mark();
pop_spot();
node = bufsubstr();
}
else
{
go_right_1 ();
skip_white();
if (eolp())
{
go_right_1 ();
skip_white();
}
push_mark();
if (looking_at_char('(')) () = ffind_char (')');
% comma, tab, '.', or newline terminates
skip_chars ("^,.\t\n");
%skip_chars("\d032-\d043\d045\d047-\d255");
bskip_chars(" ");
node = bufsubstr(());
pop_spot();
}
info_find_node(node);
}
define info_menu ()
{
variable node, colons, colon;
node = Null_String;
colon = ":"; colons = "::";
if ((LAST_CHAR == '\r') and re_looking_at ("\\C*Note[ \t\n]"))
%and not(bolp ()))
{
go_right (5); skip_chars (" \t\n");
info_follow_current_xref ();
return;
}
info_find_menu (0);
bol ();
if (looking_at("* ")
and (ffind(colon)))
{
push_mark();
bol(); go_right(2);
node = bufsubstr() + colon;
bol ();
}
ifnot (strlen (node) and (LAST_CHAR == '\r'))
{
node = read_mini("Menu item:", node, Null_String);
info_find_menu (1);
}
ifnot (bol_fsearch("* " + node)) error ("Menu Item not found.");
ifnot (ffind(colon)) error ("Corrupt File?");
if (looking_at(colons))
{
push_mark();
bol(); go_right(2);
}
else
{
go_right_1 ();
skip_white();
push_mark();
if (looking_at_char('('))
{
() = ffind_char (')');
}
% comma, tab, '.', or newline terminates
skip_chars ("^,.\t\n");
%skip_chars("\d032-\d043\d045\d047-\d255");
bskip_chars(" ");
}
info_find_node(bufsubstr(()));
}
define info_find_dir()
{
info_find_node ("(DIR)top");
}
define info_up ()
{
bob();
ifnot (ffind("Up: "))
{
info_find_dir ();
return;
}
go_right(4); push_mark();
% comma, tab, or newline terminates
skip_chars ("^,.\t\n");
%skip_chars("\d032-\d043\d045-\d255");
bskip_chars(" ");
info_find_node(bufsubstr(()));
}
define info_prev()
{
variable n; n = 10;
bob();
ifnot (ffind("Previous: "))
{
ifnot (ffind("Prev: ")) error ("Node has no PREVIOUS");
n = 6;
}
go_right(n); push_mark();
skip_chars ("^,.\t\n");
%skip_chars("\d032-\d043\d045-\d255");
bskip_chars(" ");
info_find_node(bufsubstr(()));
}
define info_goto_last_position ()
{
variable split_file, file, n;
variable pos;
if (Info_Stack_Depth == 0) return;
--Info_Stack_Depth;
pos = Info_Position_Stack [Info_Stack_Depth];
split_file = pos.split_filename;
file = pos.filename;
n = pos.line_number;
if ((file == Info_This_Filename) and bufferp("*Info*"))
{
widen();
goto_line(n); bol();
info_narrow();
return;
}
if (strlen(split_file))
{
setbuf(" *Info*");
set_readonly(0);
widen();
erase_buffer();
#ifndef VMS
variable ext = info_is_compressed (split_file);
if (strlen(ext))
() = run_shell_cmd(sprintf("%s %s%s", make_unzip_cmd (ext), split_file, ext));
else
#endif
() = insert_file (split_file);
Info_Split_File_Buffer = whatbuf ();
setbuf ("*Info*");
}
ifnot (strlen(file)) return;
info_find_file(file);
goto_line(n); bol();
info_narrow();
}
define info_next ()
{
bob();
ifnot (ffind("Next: "))
{
info_goto_last_position ();
message ("Node has no NEXT.");
return;
}
go_right(6); push_mark();
% comma, tab, or newline terminates
skip_chars ("^,.\t\n");
%skip_chars("\d032-\d043\d045-\d255");
bskip_chars(" ");
info_find_node(bufsubstr(()));
}
define info_quick_help()
{
message("q:quit, h:tutorial, SPC:next screen, DEL:prev screen, m:menu, s:search");
}
$2 = "Infomap";
ifnot (keymap_p($2))
{
make_keymap($2);
definekey("info_quick_help", "?", $2);
definekey("info_tutorial", "h", $2);
definekey("info_tutorial", "H", $2);
definekey("info_menu", "^M", $2);
definekey("info_menu", "M", $2);
definekey("info_menu", "m", $2);
definekey("info_next_xref", "\t", $2);
#ifdef MSDOS MSWINDOWS
definekey("info_prev_xref", "^@^O", $2);
#endif
definekey("info_next", "N", $2);
definekey("info_next", "n", $2);
definekey("info_prev", "P", $2);
definekey("info_prev", "p", $2);
definekey("info_up", "U", $2);
definekey("info_up", "u", $2);
definekey("page_down", " ", $2);
definekey("page_up", "^?", $2);
definekey("bob", "B", $2);
definekey("bob", "b", $2);
definekey("info_goto_node", "G", $2);
definekey("info_goto_node", "g", $2);
definekey("info_quit", "q", $2);
definekey("info_quit", "Q", $2);
definekey("info_goto_last_position", "l", $2);
definekey("info_goto_last_position", "L", $2);
definekey("info_search", "S", $2);
definekey("info_search", "s", $2);
definekey("info_search", "/", $2);
definekey("info_follow_reference", "f", $2);
definekey("info_follow_reference", "F", $2);
definekey("info_find_dir", "D", $2);
definekey("info_find_dir", "d", $2);
_for (1, 9, 1)
{
$1 = ();
definekey("info_menu_number", string($1), $2);
}
}
define info_quit ()
{
info_record_position();
widen();
delbuf("*Info*");
}
define info_goto_node()
{
info_find_node (read_mini("Node:", Null_String, Null_String));
}
define info_search ()
{
variable this_line, this_file, str, err_str, file, wline, ifile, ext;
err_str = "String not found.";
str = read_mini("Re-Search:", LAST_SEARCH, Null_String);
ifnot (strlen(str)) return;
save_search_string(str);
widen(); go_right_1 ();
if (re_fsearch(str))
{
info_narrow();
return;
}
%
% Not found. Look to see if this is split.
%
ifnot (strlen(Info_Split_File_Buffer))
{
info_narrow();
error (err_str);
}
this_file = Info_This_Filename;
this_line = what_line();
wline = window_line(); %need this so state can be restored after a failure.
setbuf(Info_Split_File_Buffer); widen(); bob();
bol_fsearch("Indirect:"); pop();
push_mark();
if (info_search_marker(1)) go_up_1 (); else eob();
narrow();
bob();
bol_fsearch(extract_filename(this_file)); pop();
ERROR_BLOCK
{
widen();
info_find_file (this_file);
goto_line(this_line); eol();
info_narrow();
recenter(wline);
}
while (down_1 ())
{
% bol(); --- implicit
push_mark();
ifnot (ffind_char (':')) {pop_mark_0 (); break; }
file = bufsubstr();
flush("Searching " + file);
(ifile, ext) = info_make_file_name(file);
#ifdef UNIX OS2
if (strlen(ext))
{
variable re = str;
% Not all greps support -e option. So, try this:
if (re[0] == '-') re = "\\" + re;
setbuf(" *Info*zcat*"); erase_buffer();
() = run_shell_cmd(sprintf("%s %s%s | grep -ci '%s'",
make_unzip_cmd (ext),
ifile, ext,
re));
bob();
if (looking_at_char ('0'))
{
delbuf(whatbuf());
setbuf(Info_Split_File_Buffer);
continue;
}
setbuf(Info_Split_File_Buffer);
}
else
#endif
ifnot (search_file(ifile, str, 1))
{
setbuf(Info_Split_File_Buffer);
continue;
}
info_find_file(file);
pop(fsearch(str));
info_narrow();
info_push_position(this_file, Info_Split_Filename, this_line);
return;
}
error (err_str);
}
define info_looking_at (ref)
{
variable n;
variable word;
push_spot ();
EXIT_BLOCK
{
pop_spot ();
}
ref = strcompress (ref, " ");
n = 0;
while (word = extract_element (ref, n, ' '), word != NULL)
{
n++;
skip_chars (" \t\n");
ifnot (looking_at (word)) return 0;
go_right (strlen (word));
}
1;
}
define info_follow_reference ()
{
variable colon, colons, note, err, item, node, ref;
colon = ":"; colons = "::";
note = "*Note";
err = "No cross references.";
push_spot();
ifnot (fsearch(note))
{
ifnot (bsearch(note))
{
pop_spot();
error(err);
}
}
pop_spot();
ref = read_mini("Follow *Note", Null_String, Null_String);
push_spot_bob ();
forever
{
ifnot (fsearch(note))
{
pop_spot();
error ("Bad reference.");
}
go_right (5); skip_chars (" \t\n");
% skip_white();
% if (eolp())
% {
% go_right_1 (); skip_white();
% }
if (info_looking_at(ref)) break;
}
push_mark();
pop_spot();
%info_record_position
pop_mark_1 ();
info_follow_current_xref ();
}
define info_menu_number ()
{
variable node; node = Null_String;
variable colon, colons;
colons = "::"; colon = ":";
variable n;
n = LAST_CHAR;
if ((n < '1') or (n > '9')) return (beep());
n -= '0';
info_find_menu(1);
while (n)
{
ifnot (bol_fsearch("* ")) return (beep());
if (ffind(colon)) --n; else eol();
}
if (looking_at(colons))
{
push_mark();
bol(); go_right(2);
}
else
{
go_right_1 (); skip_white(); push_mark();
if (looking_at_char('('))
{
() = ffind_char (')');
}
% comma, tab, '.', or newline terminates
skip_chars ("^,.\t\n");
%skip_chars("\d032-\d043\d045\d047-\d255");
bskip_chars(" ");
}
info_find_node(bufsubstr(()));
}
define info_tutorial()
{
info_find_node("(info)help");
}
private define start_info_reader ()
{
variable ibuf; ibuf = "*Info*";
if (Info_Stack_Depth) info_goto_last_position ();
ifnot (bufferp(ibuf)) info_find_dir();
pop2buf(ibuf);
onewindow();
if (0 == is_defined ("info_reader_hook"))
run_mode_hooks ("info_mode_hook");
else
run_mode_hooks ("info_reader_hook");
}
% Usage:
% info_reader ()
% info_reader (args);
% args[0] = file
% args[1] = node
define info_reader ()
{
variable file, node;
start_info_reader ();
if (_NARGS == 0)
return;
variable args = ();
variable nargs = length (args);
local_setkey ("exit_jed", "q");
local_setkey ("exit_jed", "Q");
if (nargs > 0)
{
file = args[0];
#ifdef UNIX
if (path_basename (file) != file)
{
variable dir = path_dirname (file);
file = path_basename (file);
Info_Directory = strcat (dir, "," + Info_Directory);
}
#endif
% Goto top incase the requested node does not exist.
info_find_node (sprintf ("(%s)top", file));
if (nargs > 1)
info_find_node (sprintf ("(%s)%s", file, args[1]));
}
}