#!/usr/bin/perl # Apollia's Navigation Command Prompt for GNU/Linux # (Navig, for short) # # Version 1.5 - March 17, 2016. # # # by Apollia of Astroblahhh.Com - http://astroblahhh.com/ # # (Most of the important changes were done long before March 17, 2016, but # on March 17, 2016, I improved v1.5 a little, wrote a few new comments, # added the GNU Affero license, and finally released v1.5.) # # Version 1.0 completed 7/31/2012. # # # Definitely works in Lucid Puppy Linux 5.2.8 version 004. # # Might work in other GNU/Linuxes, but might need to be modified a bit, # if you don't have the RXVT console software and/or the Rox-Filer # file manager. # # # Zip file: # # http://astroblahhh.com/software/perl/apollias-navigation-command-prompt-for-gnu-linux/v1.5/apollias-navigation-command-prompt-for-gnu-linux-v1.5.zip # # # Source code readable online at: # # http://astroblahhh.com/software/perl/apollias-navigation-command-prompt-for-gnu-linux/v1.5/apollias-navigation-command-prompt-for-gnu-linux-v1.5.txt # # http://astroblahhh.com/software/perl/apollias-navigation-command-prompt-for-gnu-linux/v1.5/Navig Shortcuts 1.5.txt # # # Description # ----------- # # This script opens a terminal window with a prompt where you can type # shortcut commands to quickly open any folder path using the file # manager of your choice. (Shortcuts to symlinks to folders also work.) # # You can optionally add an alternate file manager which you can switch # to using the ' command. # # You can add shortcuts to more folder paths by editing this script. # # # Optional Dependencies # --------------------- # # If you have the Perl module Proc::Daemon, you'll be able to launch file # managers other than Rox-Filer in a separate process. # # And look a bit nicer with the Perl module Text::TabularDisplay. # # But, this script will run without them. # # # http://search.cpan.org/~akreal/Proc-Daemon-0.23/lib/Proc/Daemon.pod # # http://search.cpan.org/~darren/Text-TabularDisplay-1.33/TabularDisplay.pm # # # By default, the script assumes your file manager is Rox-Filer. # But, you can change that in the settings. # # # This script assumes you have the RXVT console software. # # If you don't have that, then, to make it possible to run this script # just by double-clicking on it, you'll have to edit the variable # "$command_line_to_relaunch_this_script_in_a_new_console_window" # to make it use the console software you want to use. # # # # How to Change the Settings # -------------------------- # # To edit settings other than the list of shortcuts, scroll down to the section # in this file titled "Stuff you might want to change." # # To edit the list of shortcuts, edit the separate "Navig Shortcuts 1.5" file. # # # # Probably The Most Notable Differences between v1.0 and v1.5 # ----------------------------------------------------------- # # The main difference is, v1.5 gets the shortcut list from a separate file. # # Version 1.5 is under the GNU Affero General Public License 3, # while version 1.0 is public domain. # # #------------------------------------------------------------------------------ #| Instructions | #------------------------------------------------------------------------------ # # First, if this script is named... # # apollias-navigation-command-prompt-for-gnu-linux-1.5.txt # # ...rename it to something else, like: # # apollias-navigation-command-prompt-for-gnu-linux-1.5.pl # # Though even a name without .pl after it should work, like "Navig". # # (In Rox-Filer, you can rename files by right-clicking on them, going # to the "File '[name of the file you right-clicked on]'" menu, and # choosing Rename. Be careful to edit only the file name and not the # file path, because, annoyingly, the Rename function of Rox can do # more than just rename files - it can actually move files around if # you change the file path.) # # # You will most likely have to give this script Exec permissions to make # it possible to run this script just by double-clicking on it. # # In Lucid Puppy 5.2, if this script is on a disk or partition in NTFS # or FAT format (formats Windows likes), you might be able to make this # script executable simply by renaming this script to something that # doesn't end in .txt. # # But if this script is on a disk or partition in a Linux format like ext2, # you'll have to do a bit more to grant Exec permissions to this script. # Here's one way to do it: # # In Rox-Filer, right-click this apollias-navigation-command-prompt-for-linux.pl # script, go to the "File ' apollias-navigation-command-prompt-for-linux.pl'" # menu, and choose Properties. Then, put a check in the checkbox across from # Owner and under Exec, and click Close. # # # Now, to run this script, you should be able to just double-click on it. # But before you do, you'll probably want to customize the script a little. # # To do that, open the script in a text editor. (In many Puppy Linuxes by # default, there is an "edit" icon on the desktop which will open a # text editor named Geany.) # # Then, follow the instructions below, beginning at the section # "Stuff you might want to change". # # #------- # # Under the GNU Affero General Public License 3. # # Copyright (C) 2016 Apollia # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # # # Contact info: http://astroblahhh.com/contact-apollia.shtml # #------- ################ # # Constants for readability # use constant false => 0; use constant true => 1; # # End of Constants for readability # ################ ################ # # Script's Path - Unescaped and Escaped # # use Cwd 'abs_path'; $unescaped_script_path=abs_path($0); $escaped_script_path=Escape_Single_Quotes_for_Bash($unescaped_script_path); # # End of Script's Path section # # ################ ################ # # Stuff to Open Console Window When Script is Double-Clicked # # Here, we open a new console window so the user can see some output # if they ran the script by double-clicking on it. # $Launch_Parameter=$ARGV[0]; if (!$Launch_Parameter) # If this script is called with no arguments... { # 18:13 03/17/2016. Edit this variable if you don't have the RXVT terminal software: $command_line_to_relaunch_this_script_in_a_new_console_window="rxvt -rv +sb -sr -sl 99999 -title 'Navigation Command Prompt' -e perl '$escaped_script_path' 1"; # Default: # $command_line_to_relaunch_this_script_in_a_new_console_window="rxvt -rv +sb -sr -sl 99999 -title Navigation Command Prompt' -e perl '$escaped_script_path' 1"; # # Change +sb to -sr if you want a scrollbar. exec ("$command_line_to_relaunch_this_script_in_a_new_console_window"); # By default (unless you changed the above command line variable), # the above shell command opens an RXVT console window, and runs this # Perl script again with one argument (just a meaningless 1 at the end), # which will enable the script to continue past this section. # # The point of this section is to make it so it's possible to read the # script's output without you having had to launch the script manually # from a command line. # # Otherwise, when you double-clicked on this script in your file manager # window, no output would be displayed. # If you're having trouble with this script and need to debug it, # you might need to comment out the exec line, or else you # won't be able to read error messages because the automatically # opened RXVT window will immediately close when an error occurs. # # # How to run this script in your own terminal window in Lucid # Puppy 5.2.8: # # In Rox-Filer, go to the folder this script is in, and press ` # to open a terminal window. # # ` is the backtick key. It might be just below Esc on your # keyboard and likely also has a ~ (tilde) symbol on it. # # Opening a console window that way will conveniently set the # current working directory to the folder you were in when you # pressed `. # # Then, you can run this script by typing "perl Navig" (or # whatever you named this script), without quotes. } # # End of section where we open a new console window. # ################ ###################################################################### # # Stuff you might want to change # # ################ # # Includes # # 17:35 03/17/2016. Below, put the path to your separate shortcuts file. # $path_to_navig_shortcuts = "/root/zzz-Meadow/Forest/Veldt/Hot Spring/Lupu5284/Navig/Navig Shortcuts"; require $path_to_navig_shortcuts; # # End of Includes section # ################ $main_file_manager_shell_command="rox"; # If you're using Lucid Puppy 5.2.8 or many other Puppy Linuxes, # you can probably leave this alone. # # The default file manager in Lucid Puppy 5.2.8 (and various other # Puppy Linuxes) is Rox-Filer, whose terminal command is "rox". $should_allow_switching_to_alt_file_manager = true; # If you want to be able to switch between two different file # managers using the ' command, set the above to true, and # set $alt_file_manager_shell_command equal to a file manager # shell command like "rox" or "thunar". $alt_file_manager_shell_command="thunar"; # tuxcmd # Thunar isn't installed by default in Lucid Puppy 5.2.8, but, # you can get it here: # # http://distro.ibiblio.org/pub/linux/distributions/puppylinux/pet-packages-lucid/ # # As of 07:06 07/30/2012, there are two .pets containing Thunar - # thunar-1.0.2-1-i486.pet (421.6 KB) and Thunar-1.0.2-Lucid.pet (4.3 MB). # # Interestingly, after a very superficial look at both, it seems that # despite such a large size difference, they both work and seem about # the same. # # The only difference I've noticed so far is that the smaller one opens # things via single click by default, and the larger one's default # is double-click. $open_in_left_panel_of_tuxcmd=true; $open_in_right_panel_of_tuxcmd=false; # 19:41 03/17/2016. Wow, I forgot I added this feature. I hope it still works. $should_launch_shell_command_in_separate_process=true; # If you use a different file manager than Rox, like Thunar - you # might (or definitely in the case of Thunar) have to set this to # true, and also install the Perl module Proc::Daemon. # # (And even with Rox, doing things that way makes it seem a little # faster - not sure why.) # # If you use file managers other than Rox with this set to false # and/or without Proc::Daemon installed, this script might get # stuck, and you won't be able to type another command until you # close the folder opened by your last command, and closing this # script's terminal window might also close the non-Rox file manager # windows this script has opened. # # # Here's where you can get Proc::Daemon: # # http://search.cpan.org/~deti/Proc-Daemon-0.14/lib/Proc/Daemon.pod # # Click the Source link on that page, then save the Daemon.pm file. # # You can install it simply by putting Daemon.pm in the proper # folder. In Lucid Puppy 5.2.8, that folder is: # # /usr/share/perl/5.10.1/Proc/ # # You can go to the /usr/share/perl/5.10.1/ folder by running this script # and typing the "perl" shortcut. # # # If the Proc folder doesn't exist already, create it. (In Rox, # right-click the folder window's background, choose New, choose # Directory from the submenu, type Proc, and click Create.) # # Then, put the Daemon.pm file in the Proc folder. # # # Not all modules are so easy to install, but, fortunately, # Proc::Daemon is, at least in Lucid Puppy 5.2.8. # # A Linux distro which is more finicky about security might not let # you just add files to the appropriate folder so easily. In that # case, I'm not sure what you'd do to install the module. # # Maybe this page will help: # # http://www.cpan.org/modules/INSTALL.html $should_use_dest_folder_as_command_line_argument=true; # Setting this to true is unnecessary with Rox or Thunar, but, # you can set this to true if: # # * If you want Rox and Thunar to display your fake rather # than actual location if you type a shortcut that # goes to a symlink to a folder instead of directly to # a folder. # # * If your file manager actually requires you to provide # the destination folder as a command line argument, and # (unlike Rox or Thunar) won't just automatically go to # the current working directory when the file manager # shell command alone is issued. # # I prefer leaving this set to false because I prefer Rox to # and Thunar to give me my real location after they've followed # a symlink. $should_display_tables_of_commands_which_list_categorized_shortcuts = true; ########################################################################### # # Minor cosmetic or debugging settings: # $should_display_shortcut_description_when_going_to_shortcut_path = true; $should_announce_launching_in_separate_process = false; # If true, the script mentions "(Launched in a separate process.)" # when it's launching a command line in a separate process. # # If false, the script doesn't point that out. $should_display_command_line_to_issue = true; $should_display_notice_if_TextTabularDisplay_not_installed = true; # # # End of Minor cosmetic or debugging settings. # ################################################## # # End of Stuff you might want to change # ####################################################################### # # Nothing beyond here needs to be modified. # $script_version_number="1.5"; $file_manager_shell_command=$main_file_manager_shell_command; $file_manager_in_use="main"; if ($should_display_tables_of_commands_which_list_categorized_shortcuts == true) { if (eval "require Text::TabularDisplay") { $TextTabularDisplay_module_installed=true; $NiceFourColumnTable=MakeNiceCategorizedCommandTable(4, %four_column_table_of_commands_which_list_categorized_shortcuts); $NiceEightColumnTable=MakeNiceCategorizedCommandTable(8, %eight_column_table_of_commands_which_list_categorized_shortcuts); } else { $TextTabularDisplay_module_installed=false; } } if ($should_launch_shell_command_in_separate_process==true) { if (eval "require Proc::Daemon") { $ProcDaemon_module_installed=true; $daemon = Proc::Daemon->new ( work_dir => "$unescaped_script_folder", ); } else { $ProcDaemon_module_installed=false; } } ProvideCommandPrompt(); # 18:57 03/17/2016. The rest of this script is just function definitions. sub ProvideCommandPrompt { PrintHelpMessage(); for (;;) { if ($file_manager_shell_command ne "rox") { if ($should_launch_shell_command_in_separate_process==false || ($should_launch_shell_command_in_separate_process && $ProcDaemon_module_installed==false) ) { if ($should_launch_shell_command_in_separate_process==false) { print "\n\nWarning: You are using the $file_manager_shell_command file manager while \$should_launch_shell_command_in_separate_process is set to false.\n"; } else { print "\n\nWarning: The Perl module Proc::Daemon isn't installed, so this script is unable to launch shell commands in separate processes.\n"; } print "\nSo, this script might get stuck and not let you type any more commands until you close any $file_manager_shell_command windows you open with this script, and if you close this script's terminal window, all $file_manager_shell_command windows you opened with this script might automatically close too.\n\n"; } } if ($should_allow_switching_to_alt_file_manager && $alt_file_manager_shell_command) { print "$file_manager_shell_command "; } print "> "; $command=; ParseCommand($command); if ($command ne "g" && $command ne "" && $command ne "'") { $prev_command=$command; } }; } sub PrintHelpMessage { print "Apollia's Navigation Command Prompt v$script_version_number"; if ($should_display_tables_of_commands_which_list_categorized_shortcuts==true) { print "\n\n"; print "Categorized shortcuts can be listed by typing one of the following:\n"; print "(Don't type text in parentheses, those are just descriptions.)\n"; if ($TextTabularDisplay_module_installed==true) { print $NiceFourColumnTable->render; print "\n"; print $NiceEightColumnTable->render; } else { print "\n"; DisplayMessyTable(%four_column_table_of_commands_which_list_categorized_shortcuts); print "\n\n"; DisplayMessyTable(%eight_column_table_of_commands_which_list_categorized_shortcuts); if ($should_display_notice_if_TextTabularDisplay_not_installed) { print "\n\nThis script would display the above in better-formatted tables, but currently cannot do so because you don't have the Perl module Text::TabularDisplay installed."; } } } if ($should_launch_shell_command_in_separate_process==true && $ProcDaemon_module_installed==false) { print "\n\nWarning: The Perl module Proc::Daemon isn't installed, but you need it because you have \$should_launch_shell_command_in_separate_process set to true. Please read this script's source code comments for help with that."; print "\n\nThis script will still run, but won't be able to launch shell commands in separate processes.\n\nSo, if you're using a file manager other than Rox, this script might get stuck and you won't be able to type another command until you close the folder opened by your last command, and closing this script's terminal window might close any non-Rox file manager windows this script has launched."; print "\n\n(Fortunately, Rox doesn't have those problems, since Rox seems to automatically launch itself in a separate process no matter what.)\n"; } print "\n\nType 'sc' for a full list of shortcuts. Type 'h' to repeat this help message. Type 'g' to repeat the most recent previous command. Type 'q' to quit."; print "\n\nWill open folders with $file_manager_shell_command"; if ($should_launch_shell_command_in_separate_process && $ProcDaemon_module_installed) { print ", in a separate process"; } if ($should_allow_switching_to_alt_file_manager) { print ".\n"; if ($file_manager_in_use eq "main") { if ($alt_file_manager_shell_command) { print "Alt file manager: $alt_file_manager_shell_command. "; } } elsif ($file_manager_in_use eq "alt") { print "Main file manager: $main_file_manager_shell_command. "; } print "Type ' to switch between file managers"; } print ".\n\n"; } sub DisplayMessyTable { %this_list_of_categorized_shortcuts = @_; $linelength=0; foreach $key (sort keys %this_list_of_categorized_shortcuts) { $string_to_print = "$key (" . $this_list_of_categorized_shortcuts{$key}[-1] . ") "; $length_of_string_to_print = length($string_to_print); $linelength = $linelength + $length_of_string_to_print; if ($linelength>=60) { print "\n"; $linelength=0; } print $string_to_print; } } sub MakeNiceCategorizedCommandTable { $num_of_columns=shift(@_); %these_commands_which_list_categorized_shortcuts=@_; my $NiceTableObject=Text::TabularDisplay->new; $location_within_list=0; @row_data=(); foreach $lister_command (sort keys %these_commands_which_list_categorized_shortcuts) { push(@row_data, $lister_command); push(@row_data, "(" . $these_commands_which_list_categorized_shortcuts{$lister_command}[-1] . ")" ); # Array element -1 is the last element of the array, # which, in this case, contains the lister command description. $location_within_list=$location_within_list+2; if ($location_within_list % $num_of_columns == 0 && $location_within_list!=0 ) { # Table rows are only built when $location_within_list modulus # $num_of_column_pairs is 0 - that is, when $location_within_list # is evenly divisible by the divisor $num_of_columns, which means # we've reached the end of the row. $NiceTableObject->add(@row_data); @row_data=(); } } if (@row_data) { # If the last row didn't fill up all the columns, it didn't # get added above, so it gets added here... $NiceTableObject->add(@row_data); } return $NiceTableObject; } sub ParseCommand { $command=$_[0]; chomp($command); if ($command eq "q" || $command eq "quit") { die; return true; } elsif ($command eq "he" || $command eq "help") { PrintHelpMessage(); return true; } elsif ($command eq "'") { print "\n"; if ($should_allow_switching_to_alt_file_manager) { if ($alt_file_manager_shell_command) { if ($file_manager_in_use eq "main") { print "Changed to $alt_file_manager_shell_command"; $file_manager_in_use="alt"; $file_manager_shell_command=$alt_file_manager_shell_command; } else { print "Changed to $main_file_manager_shell_command"; $file_manager_in_use="main"; $file_manager_shell_command=$main_file_manager_shell_command; } print ".\n\n"; return true; } else { print "Can't switch file managers - no alt file manager provided. Please edit this script and set \$alt_file_manager_shell_command.\n\n"; return false; } } else { print "Couldn't switch file managers - \$should_allow_switching_to_alt_file_manager isn't set to true."; if (!$alt_file_manager_shell_command) { print " You also need to set \$alt_file_manager_shell_command."; } print "\n\n"; return false; } } elsif ($command eq "g") { if ($prev_command ne "" && $prev_command ne "g") { print "Repeating previous command: $prev_command\n"; ParseCommand($prev_command); return true; } elsif ($prev_command eq "") { print "No repeatable previous command was ever issued, so can't repeat the previous command.\n\n"; return false; } elsif ($prev_command eq "g") { print "Prev command: $prev_command This line should never be displayed, prev_command should never end up being set to g.\n\n"; return false; } } elsif ($command eq "sc") { print "\nList of all shortcuts...\n\n"; foreach $key (sort keys %shortcuts) { print "$key => $shortcuts{$key}[0]"; #$shortcuts{$key}[1]\n"; $description=$shortcuts{$key}[1]; PrintDescriptionWithIndenting(20, $description); print "\n"; } return true; } elsif (exists $shortcuts{$command} ) { $dest=$shortcuts{$command}[0]; if (-e $dest) { if (-d $dest) { chdir $dest; if ($should_use_dest_folder_as_command_line_argument==false) { $command_line_to_issue=$file_manager_shell_command; } else { $escaped_dest=$dest; $escaped_dest=Escape_Single_Quotes_for_Bash($escaped_dest); $escaped_dest="'$escaped_dest'"; $command_line_to_issue=$file_manager_shell_command . " "; if ($open_in_left_panel_of_tuxcmd==true && $file_manager_shell_command eq "tuxcmd") { $command_line_to_issue = $command_line_to_issue . " --left="; if (!$open_in_right_panel_of_tuxcmd) { $command_line_to_issue = $command_line_to_issue . $escaped_dest; } } if ($open_in_right_panel_of_tuxcmd==true && $file_manager_shell_command eq "tuxcmd") { $command_line_to_issue = $command_line_to_issue . " --right="; } $command_line_to_issue = $command_line_to_issue . $escaped_dest; } print "\nOpening $dest...\n"; if ($should_display_shortcut_description_when_going_to_shortcut_path==true) { $description=$shortcuts{$command}[1]; PrintDescriptionWithIndenting(5, $description); print "\n\n"; } if ($should_display_command_line_to_issue==true) { print "\n# $command_line_to_issue\n\n"; } if ($should_launch_shell_command_in_separate_process==false) { system ("$command_line_to_issue"); return true; } else { if ($ProcDaemon_module_installed==false) { print "\n(Unable to launch in separate process because the Perl module Proc::Daemon isn't installed.)\n\n"; system ("$command_line_to_issue"); return true; } else { if ($should_announce_launching_in_separate_process==true) { print "(Launched in a separate process.)\n\n"; } $Spawn_PID = $daemon->Init ( { work_dir => "$dest", exec_command => "$command_line_to_issue", } ); return true; } } } else { print "\nSorry, $dest isn't a directory. Can't open.\n\n"; return false; } } else { print "\nSorry, $dest doesn't exist. Can't open.\n\n"; return false; } } elsif (exists ($four_column_table_of_commands_which_list_categorized_shortcuts{$command} ) || exists ($eight_column_table_of_commands_which_list_categorized_shortcuts{$command} ) ) { if (exists ($four_column_table_of_commands_which_list_categorized_shortcuts{$command} ) ) { $array_reference=$four_column_table_of_commands_which_list_categorized_shortcuts{$command}; } elsif ( exists ($eight_column_table_of_commands_which_list_categorized_shortcuts{$command} ) ) { $array_reference=$eight_column_table_of_commands_which_list_categorized_shortcuts{$command}; } print "\n"; @categorized_command_list=@$array_reference; $array_length=@categorized_command_list; $array_length--; $lister_command_desc=@categorized_command_list[-1]; print "$lister_command_desc shortcuts:\n\n"; $x=0; while ($x<$array_length) { $thiscommand=$categorized_command_list[$x]; print "$thiscommand => $shortcuts{$thiscommand}[0]"; $description=$shortcuts{$thiscommand}[1]; PrintDescriptionWithIndenting(20, $description); print "\n"; $x++; } print "\n"; return true; } } sub PrintDescriptionWithIndenting { $num_of_indent_spaces=shift; $this_desc=shift; $desc_length=length($this_desc); $max_desc_length=80-$num_of_indent_spaces; $string_of_indent_spaces = " " x $num_of_indent_spaces; if ($desc_length>$max_desc_length) { while ($desc_length>0) { print "\n"; $desc_line=substr($this_desc, 0, $max_desc_length); $last_char_of_desc_line = substr($desc_line,-1,1); if ($last_char_of_desc_line ne " ") { $rightmost_space_in_desc_line=undef; $rightmost_space_in_desc_line=rindex($desc_line, " "); if ($rightmost_space_in_desc_line > 0 ) { $amount_to_clear=$rightmost_space_in_desc_line; $desc_line=substr($desc_line, 0, $rightmost_space_in_desc_line); } else { $amount_to_clear=$max_desc_length; } } else { $amount_to_clear=$max_desc_length; } print $string_of_indent_spaces; $desc_line=~s/^\s+//; # Removes any leading whitespace print $desc_line; substr($this_desc, 0, $amount_to_clear)=""; $desc_length=length($this_desc); } } else { print "\n"; print $string_of_indent_spaces; print $this_desc; } } sub Escape_Single_Quotes_for_Bash { my $string=$_[0]; #---- my $result; #@@@@ $result=$string; ############################## if (defined $result) { $result=~s/'/'\"'\"'/g; } else { $result=""; print "\n\n\n\nWARNING: An undefined string was passed to Escape_Single_Quotes_for_Bash()!"; } #!!!! return $result; } # End of script.