Lots of updates, updated internals to use nex:// urls for navigation and history and links. Fixed history and go back, mostly. Added

code to modify links in read index pages only to add "[>#]" to identify link number. Recorded all links on a page in an array and
used URI to handle relative links including "../index" type links (I hope, there were none to test).
Added UI to navigate by link #s I made, went with kludgy dialog box. Had the idea to use > and < for navigation to copy the '[>#]' format I added to index page link lines, which works not bad. Added some help to status bar, other stuff.

What needs to get added is some 'branding' (program name, about dialog) and a help dialog and to reorganize the menu so it's not crazy.

Also could remove some of the status dialogs.
This commit is contained in:
peteyboy 2024-03-06 04:09:19 -05:00
parent 2dec7bf629
commit e957438be6

343
connex.pl
View File

@ -1,32 +1,40 @@
#!/usr/bin/perl -w #!/usr/bin/perl -w
#connex.pl Nightfall Express (nex://) browser
#TODO: Some branding (status popup on load?, Connex menu item?), About dialog
#TODO: Help dialog
use strict; use strict;
use warnings; use warnings;
use strict; use strict;
use Curses::UI; use Curses::UI;
use Net::Telnet; use Net::Telnet;
use URI::Split; use URI::Split qw(uri_split uri_join);
use URI ();
my $HOST_DEFAULT = "nightfall.city"; my $HOST_DEFAULT = "nightfall.city";
my $PATHSPEC_DEFAULT = ''; my $PATHSPEC_DEFAULT = '';
my $PORT_DEFAULT = 1900; my $PORT_DEFAULT = 1900;
my $PROT_NEX = "nex://"; my $SCHEME_NEX = "nex";
my $home_default= $PROT_NEX . $HOST_DEFAULT; my $home_default= $SCHEME_NEX . $HOST_DEFAULT;
my $host = "nightfall.city"; my $host = "nightfall.city";
my $port = "1900"; my $port = "1900";
my $pathspec = ""; my $pathspec = "";
my $docname = ""; my $docname; # = "";
my $doctype = "txt"; my $doctype = "txt";
my $dot_ext = "."; my $dot_ext = ".";
my @history;
my $HOME_URL = uri_join($SCHEME_NEX,$HOST_DEFAULT);
my $full_url = $HOME_URL;
my @history;
my @page_links;
my $statusbar; my $statusbar;
my $navwindow;
my $win1; my $win1;
my $cui = new Curses::UI( -color_support => 1 ); my $cui = new Curses::UI( -color_support => 1 );
my $connect = new Net::Telnet (Timeout => 10, my $connect = new Net::Telnet (Timeout => 10,
@ -34,10 +42,11 @@ my $connect = new Net::Telnet (Timeout => 10,
my @menu = ( my @menu = (
{ -label => 'File', { -label => 'File',
-submenu => [ -submenu => [
{ -label => 'Go to Link ^G', -value => \&goto_link_dialog }, { -label => 'Go to Link ^G', -value => \&navigate_link_dialog },
{ -label => 'Change Site ^C', -value => \&goto_site_dialog }, { -label => 'Choose Link >', -value => \&goto_link_dialog },
{ -label => 'Back ^B', -value => \&goto_back }, { -label => 'Back ^B', -value => \&goto_back },
{ -label => 'History ^H', -value => "\&history_status_dialog" }, { -label => 'History ^H', -value => \&history_status_dialog },
{ -label => 'Page Links ^P', -value => \&page_links_dialog },
{ -label => 'Exit ^Q', -value => \&exit_dialog } { -label => 'Exit ^Q', -value => \&exit_dialog }
] ]
}, },
@ -47,8 +56,8 @@ my @menu = (
sub exit_dialog() sub exit_dialog()
{ {
my $return = $cui->dialog( my $return = $cui->dialog(
-message => "Really quit?", -title => "Quit Connex?",
-title => "Are you sure?", -message => "Are you sure?",
-buttons => ['yes', 'no'], -buttons => ['yes', 'no'],
); );
@ -56,60 +65,102 @@ sub exit_dialog()
exit(0) if $return; exit(0) if $return;
} }
sub goto_link_dialog()
sub links_dialog()
{ {
my $return = $cui->question(-question => "This is [$host/ $pathspec]. Enter destination link:", my $return = $cui->dialog(
-answer => $pathspec, -message => page_links_list(),
); -title => "Page Links",
navigate($return); -buttons => ['ok'],
);
} }
sub unsupported_dialog
sub goto_site_dialog()
{ {
my $scheme = shift;
my $return = $cui->dialog(
-message => "$scheme protocol not supported.",
-title => "Bad Navigation",
-buttons => ['ok'],
);
my $return = $cui->question(-question => "This is [$host/ $pathspec]. Visit site:", }
-answer => $host,
); sub navigate_link_dialog()
if ($return){ {
my $site = $return; my $return = $cui->question(-question => "This is [$full_url]. Enter destination link:",
if($site ne ''){ -answer => $full_url,
$host = $site; );
add_history($host); #if not user canceled then navigate
load($pathspec); if($return){
update_status_bar(); navigate($return);
} update_status_bar();
history_status_dialog();
} }
} }
sub goto_link_dialog()
{
my $return = $cui->question(-question => "Enter link #:",
);
#if not canceled, or too big or too small, goto link
if($return){
my $linkcount = scalar @page_links;
if($return <= $linkcount && $return >0){
goto_link($return);
update_status_bar();
}else{
#$browser->focus();
my $return1 = $cui->status("there is no link # " . $return);
}
#do nothing on cancel
}
}
sub goto_back() sub goto_back()
{ {
my $fetched = fetch_history(); my $fetched = fetch_history();
#if($fetched) { #if($fetched) {
$pathspec =$fetched; $full_url =$fetched;
load($pathspec); load($full_url,0); #don't move back to top
update_status_bar(); update_status_bar();
history_status_dialog(); history_status_dialog();
#} #}
} }
sub goto_link{
my $linknum = shift;
my $linkcount = scalar @page_links;
#my $element=$linknum -1; #offset for array element
#if ($element <= $#page_links){ #such a perlism, this is highest INDEX of array
if ($linknum <= $linkcount){ #such a perlism, this is highest INDEX of array
navigate($page_links[$linknum-1]); #offset from count to index
}else{
#my $browser = $win1->getobj("browser");
#$browser->focus();
my $return = $cui->status("no link # " . $linknum);
}
}
sub navigate{ sub navigate{
my $link = shift; my $link = shift;
#if ($link){ add_history($full_url); #add last link to history before going forward!
# if($link ne ''){ $full_url = $link;
add_history($pathspec); #add last link to history before going forward! load($full_url, 1); #new URL go to top of page
$pathspec = $link;
load($pathspec);
# }else {
# $pathspec = '';
# }
update_status_bar();
#}
} }
@ -118,7 +169,8 @@ sub update_status_bar
{ {
my $browser = $win1->getobj("browser"); my $browser = $win1->getobj("browser");
my $statusbar = $win1->getobj("status"); my $statusbar = $win1->getobj("status");
$statusbar->text("Current site: $host, current link: [$pathspec]"); #$full_url = construct_valid_url($SCHEME_NEX, $host, $pathspec, $docname);
$statusbar->text("$full_url" . " | Press '>' key to enter link #. ctl-g to enter nex URL. '<' to go back.");
$statusbar->draw(); $statusbar->draw();
$browser->focus(); $browser->focus();
@ -129,7 +181,6 @@ sub update_status_bar
sub add_history sub add_history
{ {
my $link = shift; my $link = shift;
print ("link $link");
push(@history, $link); push(@history, $link);
history_status_dialog(); history_status_dialog();
return; return;
@ -142,7 +193,7 @@ sub fetch_history
$latest = pop(@history); $latest = pop(@history);
return $latest; return $latest;
}else{ }else{
return($PATHSPEC_DEFAULT); return($HOME_URL);
} }
} }
@ -152,12 +203,43 @@ sub history_status_dialog
if(@history){ if(@history){
my $browser = $win1->getobj("browser"); my $browser = $win1->getobj("browser");
$browser->focus(); $browser->focus();
my $history_list = join(", ", @history); my $history_list = join("\n", @history);
my $return = $cui->status("$history_list"); my $return = $cui->status("history:\n$history_list");
} }
} }
sub page_links_dialog
{
if(@page_links){
my $browser = $win1->getobj("browser");
$browser->focus();
#my $link_list = join("\n", @page_links);
my $link_list = page_links_list();
my $return = $cui->status("links on this page:\n$link_list");
}
}
sub page_links_list
{
if(@page_links){
my $browser = $win1->getobj("browser");
$browser->focus();
#my $link_list = join("\n", @page_links);
my $link_list="";
my $link;
my $count=0;
foreach $link (@page_links){
$count+=1;
$link_list = $link_list . "[$count] $link\n";
}
return $link_list;
}
}
my $menu = $cui->add( my $menu = $cui->add(
'menu','Menubar', 'menu','Menubar',
-menu => \@menu, -menu => \@menu,
@ -173,6 +255,9 @@ $win1 = $cui->add(
-vscrollbar => 'right', -vscrollbar => 'right',
); );
my $texteditor = $win1->add("browser", "TextViewer", my $texteditor = $win1->add("browser", "TextViewer",
-text => "Start Page", -text => "Start Page",
-border => 1, -border => 1,
@ -195,7 +280,7 @@ $statusbar = $win1->add("status", "TextViewer",
-width => -1, -width => -1,
-reverse => 1, -reverse => 1,
-paddingspaces => 1, -paddingspaces => 1,
-text => "Current site: $HOST_DEFAULT, current link: [/]", -text => "$HOME_URL | Press '>' key to enter link #. ctl-g to enter nex URL. '<' to go back.",
); );
@ -204,78 +289,120 @@ $statusbar = $win1->add("status", "TextViewer",
#key bindings, should match menu items #key bindings, should match menu items
$cui->set_binding(sub {$menu->focus()}, "\cX"); $cui->set_binding(sub {$menu->focus()}, "\cX");
$cui->set_binding( \&exit_dialog , "\cQ"); $cui->set_binding( \&exit_dialog , "\cQ");
$cui->set_binding( \&goto_link_dialog , "\cG"); $cui->set_binding( \&navigate_link_dialog , "\cG");
$cui->set_binding( \&goto_back , "\cB"); $cui->set_binding( \&goto_back , "\cB");
$cui->set_binding( \&goto_site_dialog , "\cC"); $cui->set_binding( \&goto_back , "<");
$cui->set_binding( \&goto_link_dialog , ">");
$cui->set_binding(sub {
my $cui = shift;
$cui->layout;
$cui->draw;
}, "\cL");
# There is no need for the editor widget to loose focus, so
# the "loose-focus" binding is disabled here. This also enables the
# use of the "TAB" key in the editor, which is nice to have.
$texteditor->clear_binding('loose-focus');
#start up #start up
$texteditor->focus(); #$texteditor->focus();
navigate(''); navigate($HOME_URL);
$cui->mainloop(); $cui->mainloop();
#pick apart nex:// urls, which we should be using
#sub parse_url #need to pass in url; and host, pathspec, file variables to be filled
#{
# my $url = @_[0];
# my $host = url;
# host =~ s/^nex://([a-z0-9]*)\/[a-z0-9\/\.]*$/$1/;
# my $pathspec = url;
# pathspec =~ s/^nex://[a-z0-9]*(\/[a-z0-9\/]*)[[a-z0-9\.]*$)/$1/;
# my $file = $url
#
#}
#make nex:// urls from parts, relative urls #make nex:// urls from parts, relative urls
#sub construct_valid_url #protocol, host, pathspec sub construct_valid_url #scheme, host, pathspec, docname
#{ {
# my $protocol = shift; my $scheme = shift;
# my $host = shift; my $host = shift;
# my $pathspec = shift; my $pathspec = shift;
# my $file = shift; my $docname = shift;
# my $url = $protocol; if (defined($docname)){
#need error handling $pathspec= $pathspec . '/' . $docname; #this is just local pathspec
#what about the end slash and files vs index? }
# $url = $protocol . $host; my $url = uri_join($scheme, $host, $pathspec);
# if ($pathspec){ return $url;
# $url = $url . '/' . $pathspec; }
# if ($file){
# $url = $url . '.' $file;
# }
# }
# return $url;
#}
sub is_path_index{
my $path = shift;
my $result = 1;
if ($path !~ /\/$/ && $path =~ /\.[a-zA-Z]*$/){
$result =0;
}
return($result);
}
sub add_page_link{
my $linkline = shift;
my $base_url = shift;
my $uri_object;
#my $link= $linkline=~ s/^=>[ ]*(.*$)/$1/r;
local $URI::ABS_ALLOW_RELATIVE_SCHEME = 1;
local $URI::ABS_REMOTE_LEADING_DOTS = 1;
$uri_object=URI->new_abs($linkline=~ /^=>[ ]+([^ ]*)/,$base_url);
push(@page_links, $uri_object->as_string);
my $count = scalar @page_links; #scalar is size/count of links, but $#page_links is highest INDEX
return $linkline =~ s/(^=>.*$)/$1 [\>$count\]/r;
}
sub load sub load
{ {
my $pathspec = shift;
my @lines; my @lines;
my $ok; my $ok;
$connect->host($host); my $url = shift;
$connect->port($port); my $top = shift;
$ok= $connect->open($host); #my $scheme, $host, $path, $query, $frag;
$ok= $connect->print($pathspec); my ($scheme, $host, $path, $query, $frag) = uri_split($url);
@lines =$connect->getlines(ErrMode=> 'return'); #what happens with different scheme?
print $connect->eof(); if ($scheme eq $SCHEME_NEX){
#handle non-existant request? $connect->host($host);
die unless $connect->eof(); $connect->port($port);
$connect->close(); $ok= $connect->open($host);
my $widget= $texteditor; $ok= $connect->print($path);
my $loaded_text =""; @lines =$connect->getlines(ErrMode=> 'return');
my $currentline; print $connect->eof();
my $count=0; #handle non-existant request?
foreach $currentline (@lines){ die unless $connect->eof();
if ($currentline =~ m/^=>/){ $connect->close();
$count+=1; my $widget= $texteditor;
$currentline =~ s/(^=>.*$)/$1 [\>$count\]/;
} #loop through nex response and load into text editor. Identify, mark and store link lines for index pages
$loaded_text= $loaded_text . $currentline; #TODO: store link lines for index pages
} my $page_contents =""; #this is the page contents
$widget->text($loaded_text); my $currentline;
my $count=0;
my $is_index= is_path_index($path);
undef @page_links; #clear list
foreach $currentline (@lines){
if ($currentline =~ m/^=>/){
if ($is_index){
$currentline = add_page_link($currentline, $url);
}
}
$page_contents= $page_contents . $currentline;
}
$widget->text($page_contents);
$widget->draw();
if ($top){
$widget->pos(0);
}
}else{
#can't load non-nex as of now
my $browser = $win1->getobj("browser");
$browser->focus();
my $return = unsupported_dialog($scheme);
#pop and trash history
my $fetched = fetch_history();
}
} }