Using completion function in Term::ReadLine::Gnu - perl

I want to make a console and change the automatic completion function when I press tab but I want to differentiate between two cases:
If I press tab and the beginning of the command matches a list I supplied in an array, the auto complete will be according to this array.
If I press tab and the command isn't recognized from the list I supplied, I want the generic completion function to work, so t hat it will auto complete directories and file names in the current directory.
Is it possible?
Thanks a lot.
Edit: I'm trying to do it inside a perl script. I saw this example:
rl_attempted_completion_function
A reference to an alternative function to create matches.
The function is called with TEXT, LINE_BUFFER, START, and END. LINE_BUFFER is a current input buffer string. START and END are indices in LINE_BUFFER saying what the boundaries of TEXT are.
If this function exists and returns null list or undef, or if this variable is set to undef, then an internal function rl_complete() will call the value of $rl_completion_entry_function to generate matches, otherwise the array of strings returned will be used.
The default value of this variable is undef. You can use it as follows;
use Term::ReadLine;
...
my $term = new Term::ReadLine 'sample';
my $attribs = $term->Attribs;
...
sub sample_completion {
my ($text, $line, $start, $end) = #_;
# If first word then username completion, else filename completion
if (substr($line, 0, $start) =~ /^\s*$/) {
return $term->completion_matches($text,
$attribs->{'username_completion_function'});
} else {
return ();
}
}
...
$attribs->{attempted_completion_function} = \&sample_completion;
completion_matches(TEXT, ENTRY_FUNC)
What I want to do is that in case when tab is pressed it recognizes a substring from an array I provide, the auto completion will be from that array (if there are multiple matches it will give all of them like a regular unix console).
Otherwise, I want the auto completion to be file recognition.

The subroutine used internally by Term::ReadLine::Gnu to provide the default completion is filename_completion_function, which you can call directly from your custom subroutine:
use Term::ReadLine;
my $term = new Term::ReadLine 'MyTerm';
$term->Attribs->{'completion_entry_function'} = \&my_completion;
my $ans = $term->readline('How can I help you? ');
sub my_completion {
my ($text, $state) = #_;
if (my_test) {
return my_custom_stuff;
}
else {
return Term::ReadLine::Gnu->filename_completion_function($text, $state);
}
}

Related

Return variable when call from nested backticks with #ARGV

In visudo Ubuntu I whitelist this program (I doing this way for security purpose, parameterized all commands)
myuser ALL=(root) NOPASSWD:/App/Filter_Parameters_Wrap.pm *
In program.pl
my $capture = qx("/usr/bin/sudo /App/Filter_Parameters_Wrap.pm kernel_version");
In the module Filter_Parameters_Wrap:
my $fuction = $ARGV[0];
print filters_dispatch($fuction) if defined $fuction;
sub filters_dispatch {
my $filter = shift;
my $dispatch = {
kernel_version => \&filter_kernel_version,
};
return $dispatch->{$filter}->();
}
sub filter_kernel_version {
my $command = '/bin/uname -a';
my $sudo = App::Sudo::Main_Sudo->root($command);
utf8::decode($sudo);
return $sudo;
}
This approach is working , but I have to do print in print filters_dispatch (print directly a variable string), so I can get the output of return of function filter_kernel_version in the variable $capture
In some cases inside the function filter_kernel_version I want to create a hash and return as anonymous hash without print directly, but this way is not working
can you recommend a better approach?
No matter what option you use to communicate between processes, you'll be limited to sending a sequence of bytes. This means that you will need to serialize your hash somehow. Encoding it using JSON (e.g. using Cpanel::JSON::XS) might be a simple way of doing that.

Extracting Single from huge Archive using Perl

I'm trying a single from a large ".tgz" file. I'm using Archive::Tar::Streamed module.
Here is the sample code.
my $tar2 = Archive::Tar::Streamed->new($filename);
$fil = $tar2->next;
while($fil) {
$_ = $fil->name;
if(m/abc\.txt/g) {
$fil->extract($outpath);
$fil = $tar2->next;
}
}
But the iterator is not working. It is looping the first file in the archive not moving to the next file.
Can someone tell me what mistake i've done here???
You put the call to next inside your if, so it's only executed if you extracted the file. There's nothing that modifies $fil inside the loop if the file is not extracted.
You can simplify your code quite a bit by just calling the iterator in the condition of the while loop. Also, you can use the =~ binding operator instead of storing the name in $_. And you do not want the /g regex modifier here. In scalar context, you use /g to loop through multiple matches in a string. Here, all you want is to know whether the string contains a match.
my $tar2 = Archive::Tar::Streamed->new($filename);
while(my $fil = $tar2->next) {
if($fil->name =~ m/abc\.txt/) {
$fil->extract($outpath);
}
}

Perl subroutine array and scalar variable parameters

How exactly can I pass both scalar variables and array variables to a subroutine in Perl?
my $currVal = 1;
my $currValTwo = 1;
my #currArray = ('one','two','three');
my #currArrayTwo =('one','two','three');
&mysub($currVal, $currValTwo,\#currArray, \#currArrayTwo);
sub mysub() {
# That doesn't work for the array as I only get the first element of the array
my($inVal, $inValTwo, #inArray, #inArrayTwo) = #_;
}
You need to fetch them as references because you've already passed them as references (by using the \ operator):
my($inVal, $inValTwo, $inArray, $inArrayTwo) = #_;
and then use the references as arrays:
#{$inArray}
You pass the arguments as references, so you need to dereference them to use the values. Be careful about whether you want to change the original array or not.
sub mysub {
my($inVal, $inValTwo, $inArray, $inArrayTwo) = #_;
#{$inArrayTwo} = ('five','six','seven');
}
This will change the original #currArrayTwo, which might not be what you want.
sub mysub {
my($inVal, $inValTwo, $inArray, $inArrayTwo) = #_;
my #ATwo = #{$inArrayTwo};
#ATwo = ('five','six','seven');
}
This will only copy the values and leave the original array intact.
Also, you do not need the ampersand in front of the sub name, from perldoc perlsub:
If a subroutine is called using the &
form, the argument list is optional,
and if omitted, no #_ array is set up
for the subroutine: the #_ array at
the time of the call is visible to
subroutine instead. This is an
efficiency mechanism that new users
may wish to avoid.
You do not need empty parens after your sub declaration. Those are used to set up prototypes, which is something you do not need to do, unless you really want to.
So, for example: This is a using statement to search something in an array:
use List::Util qw(first);
This is the sub declaration:
sub GetIndex($$$);
This is the call to the sub (last parameter is: Default index value to give back if not found)
$searchedIndex = GetIndex(\#theArr, "valuesearched", 1);
This is the routine:
sub GetIndex($$$)
{
my $inArray=shift;
my #theArray= #{$inArray};
my $searchedTag= shift;
my $defaultVal= shift;
my $retVal = first { $theArray[$_] eq $searchedTag} 0 .. $#theArray;
if ((! defined $retVal)|| ($retVal<0)||($retVal>#theArray))
{
$retVal = $defaultVal;
}
return $retVal;
}

Why does Perl complain "can't modify non-lvalue subroutine call"?

I have index.pl and subs.pl. When I run the program, the user inserts the date of birth and then it is passed to the getage() subroutine in subs.pl, which has many subroutines.
getage() than implicitly calls another subroutine called validate() which validates the date entered by user.
When I run the index.pl and the user enters the date as 03-04-2005, the following error comes out:
can't modify non-lvalue subroutine call at subs.pl line 85, <> line 1
At 85th line of subs.pl I have:
list(my $val,my #value) = validate($dob);
validate() returns a message and the date($dob) which is sent from getage().
Some code from validate():
sub validate {
my $dob = shift;
my $error;
my #test;
#test = split("-",$dob);
if (!#test) {
$error = "date separator should be - ";
return ($error,#test);
}
...
The solution seems to be:
my ($val, #value) = validate($dob);
based on my intuitive understanding of what that code intended - but I can't be certain till you provide some more context (what does validate() return, what does list() mean?)
If you meant list() as a means to force $val and #value into a list, you merely need to enclose both in parenthesis: ($val, #value) to do that
An lvalue is a variable you can modify. (one that can be on the left side of an assignment, hence the name). In most circumstances, a value returned by a sub is not one you can modify.
In your example, you are attempting exactly that: assigning the return value of validate($dob) to the nonmodifiable return value of list($val, #value).
in line
list(my $val,my #value) = validate($dob);
remove 'list' and it works fine
ie
(my $val,my #value) = validate($dob);
thanks to Kayra and others who answered

How can I cleanly handle error checking in Perl?

I have a Perl routine that manages error checking. There are about 10 different checks and some are nested, based on prior success. These are typically not exceptional cases where I would need to croak/die. Also, once an error occurs, there's no point in running through the rest of the checks.
However, I can't seem to think of a neat way to solve this issue except by using something analogous to the following horrid hack:
sub lots_of_checks
{
if(failcond)
{
goto failstate:
}
elsif(failcond2)
{
goto failstate;
}
#This continues on and on until...
return 1; #O happy day!
failstate:
return 0; #Dead...
}
What I would prefer to be able to do would be something like so:
do
{
if(failcond)
{
last;
}
#...
};
An empty return statement is a better way of returning false from a Perl sub than returning 0. The latter value will actually be true in list context:
sub lots_of_checks {
return if fail_condition_1;
return if fail_condition_2;
# ...
return 1;
}
Perhaps you want to have a look at the following articles about exception handling in perl5:
perl.com: Object Oriented Exception Handling in Perl
perlfoundation.com: Exception Handling in Perl
You absolutely can do what you prefer.
Check: {
last Check
if failcond1;
last Check
if failcond2;
success();
}
Why would you not use exceptions? Any case where the normal flow of the code should not be followed is an exception. Using "return" or "goto" is really the same thing, just more "not what you want".
(What you really want are continuations, which "return", "goto", "last", and "throw" are all special cases of. While Perl does not have full continuations, we do have escape continuations; see http://metacpan.org/pod/Continuation::Escape)
In your code example, you write:
do
{
if(failcond)
{
last;
}
#...
};
This is probably the same as:
eval {
if(failcond){
die 'failcond';
}
}
If you want to be tricky and ignore other exceptions:
my $magic = [];
eval {
if(failcond){
die $magic;
}
}
if ($# != $magic) {
die; # rethrow
}
Or, you can use the Continuation::Escape module mentioned above. But
there is no reason to ignore exceptions; it is perfectly acceptable
to use them this way.
Given your example, I'd write it this way:
sub lots_of_checks {
local $_ = shift; # You can use 'my' here in 5.10+
return if /condition1/;
return if /condition2/;
# etc.
return 1;
}
Note the bare return instead of return 0. This is usually better because it respects context; the value will be undef in scalar context and () (the empty list) in list context.
If you want to hold to a single-exit point (which is slightly un-Perlish), you can do it without resorting to goto. As the documentation for last states:
... a block by itself is semantically identical to a loop that executes once.
Thus "last" can be used to effect an early exit out of such a block.
sub lots_of_checks {
local $_ = shift;
my $all_clear;
{
last if /condition1/;
last if /condition2/;
# ...
$all_clear = 1; # only set if all checks pass
}
return unless $all_clear;
return 1;
}
If you want to keep your single in/single out structure, you can modify the other suggestions slightly to get:
sub lots_of_checks
{
goto failstate if failcond1;
goto failstate if failcond2;
# This continues on and on until...
return 1; # O happy day!
failstate:
# Any clean up code here.
return; # Dead...
}
IMO, Perl's use of the statement modifier form "return if EXPR" makes guard clauses more readable than they are in C. When you first see the line, you know that you have a guard clause. This feature is often denigrated, but in this case I am quite fond of it.
Using the goto with the statement modifier retains the clarity, and reduces clutter, while it preserves your single exit code style. I've used this form when I had complex clean up to do after failing validation for a routine.