2020-06-15 19:42:13 +00:00
|
|
|
#!/usr/bin/perl
|
|
|
|
# License: AGPLv3-or-later
|
|
|
|
# see files COPYRIGHT, LICENSE, and AGPL-3.0.txt included in canonical repository for details.
|
|
|
|
# https://k.sfconservancy.org/NPO-Accounting/beancount-tools-in-Perl
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use autodie qw(:all);
|
|
|
|
|
2020-08-31 03:14:54 +00:00
|
|
|
use sigtrap 'handler' => \&CleanupEvertything, 'normal-signals', 'stack-trace' => 'error-signals';
|
|
|
|
|
|
|
|
use Carp;
|
|
|
|
|
2020-06-15 19:42:13 +00:00
|
|
|
use Getopt::Long;
|
|
|
|
use File::Spec::Functions;
|
2020-08-31 03:14:54 +00:00
|
|
|
use File::Temp qw/:mktemp tempdir/;
|
|
|
|
use Git::Repository 'Log';
|
2020-06-15 19:42:13 +00:00
|
|
|
|
|
|
|
use POSIX qw(mkfifo);
|
|
|
|
use IPC::Shareable;
|
Switch from IPC::Run to Expect module; set PAGER to /usr/bin/cat
After some efforts to make this current incarnation of the daemon
work for situations with excessively long output, I discovered two
problems.
First, bean-query seems to always use a pager in interactive mode,
and it seems impossible to dissuade bean-query from this behavior
without an upstream change to Beancount. As such, my first effort
was to force the pager to be `/usr/bin/cat`, which I thought would be
sufficient to solve the problem entirely.
After that change coupled with much debugging with IPC::Run, I found
that detecting the beancount> prompt using a regular expression of
/^\s*beancount\s*\>\s*/m, or similar efforts with simply
/\s*beancount\s*/ or other versions like /\s*beancount\s*\>\s*$/ms
/\s*beancount\s*\>\s*$/s, simply would not detect the end of the
string, so the process hung on this line in the main loop:
pump $runningBeanQuery until $rbcOut =~ /^\s*beancount\s*\>\s*/m;
Ironically, when there is *not* copious output, that line worked just
fine. It was baffling.
I switched to Expect, and found the exact same behavior with:
$runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*')
with that line, it works fine in the StartRunningBeanQuery()
subroutine, but when used in the main loop, Expect never finds that
string. Again, I tried it with various regexes as specified above to
no avail.
*However*, merely changing the line to:
$runningBeanQuery->expect(undef, 'beancount>')
(i.e., not using the regex feature in the Expect library), fixed the
problem entirely.
My working theory is some complex interaction bug between the IO::Pty
module, regular expression matching, and output timing (since this
problem only shows up when there is copious output). I think in
particular the fact that bean-query creates a subprocess for the
`PAGER` command in the pty when the output is copious is somehow
related to the problem.
Regardless, this solution now works in all the cases where I'm using
the daemon, so I'm not investigating further.
2020-08-17 15:46:57 +00:00
|
|
|
use Expect;
|
2020-08-10 13:37:08 +00:00
|
|
|
|
2020-08-31 03:14:54 +00:00
|
|
|
my $RSYNC_CMD = '/usr/bin/rsync';
|
|
|
|
|
Switch from IPC::Run to Expect module; set PAGER to /usr/bin/cat
After some efforts to make this current incarnation of the daemon
work for situations with excessively long output, I discovered two
problems.
First, bean-query seems to always use a pager in interactive mode,
and it seems impossible to dissuade bean-query from this behavior
without an upstream change to Beancount. As such, my first effort
was to force the pager to be `/usr/bin/cat`, which I thought would be
sufficient to solve the problem entirely.
After that change coupled with much debugging with IPC::Run, I found
that detecting the beancount> prompt using a regular expression of
/^\s*beancount\s*\>\s*/m, or similar efforts with simply
/\s*beancount\s*/ or other versions like /\s*beancount\s*\>\s*$/ms
/\s*beancount\s*\>\s*$/s, simply would not detect the end of the
string, so the process hung on this line in the main loop:
pump $runningBeanQuery until $rbcOut =~ /^\s*beancount\s*\>\s*/m;
Ironically, when there is *not* copious output, that line worked just
fine. It was baffling.
I switched to Expect, and found the exact same behavior with:
$runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*')
with that line, it works fine in the StartRunningBeanQuery()
subroutine, but when used in the main loop, Expect never finds that
string. Again, I tried it with various regexes as specified above to
no avail.
*However*, merely changing the line to:
$runningBeanQuery->expect(undef, 'beancount>')
(i.e., not using the regex feature in the Expect library), fixed the
problem entirely.
My working theory is some complex interaction bug between the IO::Pty
module, regular expression matching, and output timing (since this
problem only shows up when there is copious output). I think in
particular the fact that bean-query creates a subprocess for the
`PAGER` command in the pty when the output is copious is somehow
related to the problem.
Regardless, this solution now works in all the cases where I'm using
the daemon, so I'm not investigating further.
2020-08-17 15:46:57 +00:00
|
|
|
# We have to set the PAGER to a passthrough text program to assure that
|
|
|
|
# output does not get paused
|
|
|
|
$ENV{PAGER} = "/usr/bin/cat";
|
2020-06-15 19:42:13 +00:00
|
|
|
|
|
|
|
my $BEANCOUNT_QUERY_CMD = "/usr/bin/bean-query";
|
|
|
|
|
2020-08-31 03:14:54 +00:00
|
|
|
my($VERBOSE, $BEANCOUNT_DIR, $LOAD_FILE, $BRANCH_NAME, $FIFO_DIR) = (0, undef, undef, undef, undef);
|
2020-06-15 19:42:13 +00:00
|
|
|
|
|
|
|
GetOptions("verbose=i" => \$VERBOSE, "beancountDir=s" => \$BEANCOUNT_DIR,
|
2020-08-31 03:14:54 +00:00
|
|
|
"loadFile=s" => \$LOAD_FILE, "branchName=s" => \$BRANCH_NAME, 'fifoDir=s' => \$FIFO_DIR);
|
2020-06-15 19:42:13 +00:00
|
|
|
|
|
|
|
sub UsageAndExit($) {
|
2020-08-31 03:14:54 +00:00
|
|
|
print STDERR "usage: $0 --loadFile=/path/to/file.beancount --beancountDir=/path/to/beancountdir --fifoDir=/path/to/directory/for/fifos [ --branchName=BRANCH_NAME --verbose=N ]\n";
|
2020-06-15 19:42:13 +00:00
|
|
|
print STDERR "\n $_[0]\n";
|
|
|
|
exit 2;
|
|
|
|
}
|
|
|
|
UsageAndExit("/path/to/direcotry/for/fifos must be provided and be an existing directory")
|
|
|
|
unless (defined $FIFO_DIR and -d $FIFO_DIR);
|
|
|
|
|
|
|
|
UsageAndExit("/path/to/file.beancount/path/to/beancountdir must be provided and be an existing directory")
|
|
|
|
unless (defined $BEANCOUNT_DIR and -d $BEANCOUNT_DIR);
|
|
|
|
chdir $BEANCOUNT_DIR;
|
|
|
|
|
|
|
|
UsageAndExit("/path/to/file.beancount must a relative path to a file that exists in $BEANCOUNT_DIR")
|
|
|
|
unless (defined $LOAD_FILE and -r $LOAD_FILE);
|
|
|
|
|
2020-08-31 03:14:54 +00:00
|
|
|
my($tempRepository, $tempRepositoryDirectory);
|
|
|
|
|
|
|
|
sub CleanupEvertything {
|
|
|
|
$tempRepository = undef if (defined $tempRepository);
|
|
|
|
$tempRepositoryDirectory = undef if (defined $tempRepositoryDirectory);
|
|
|
|
StopRunningBeanQuery();
|
|
|
|
croak @_;
|
|
|
|
}
|
|
|
|
if (defined $BRANCH_NAME) {
|
|
|
|
my $absGitRepositoryDirectory = File::Spec->rel2abs( $BEANCOUNT_DIR );
|
|
|
|
$tempRepositoryDirectory = tempdir('beancountquerygoofydaemongit_' . $$ . '_XXXXXXXXXXX',
|
|
|
|
TMPDIR => 1, CLEANUP => 1);
|
|
|
|
print STDERR "Copy Git repository to $tempRepositoryDirectory...." if $VERBOSE > 2;
|
|
|
|
system($RSYNC_CMD, '-Ha', "$absGitRepositoryDirectory/", "$tempRepositoryDirectory/");
|
|
|
|
print STDERR "copy completed.\n" if $VERBOSE > 2;
|
|
|
|
$tempRepository = Git::Repository->new( work_tree => $tempRepositoryDirectory );
|
|
|
|
|
|
|
|
chdir $tempRepositoryDirectory;
|
|
|
|
$tempRepository->run(clean => '-fx', { quiet => 1 });
|
|
|
|
$tempRepository->run(reset => '--hard', { quiet => 1 });
|
|
|
|
$tempRepository->run(clean => '-fx', { quiet => 1 });
|
|
|
|
$tempRepository->run(checkout => '$BRANCH_NAME', { quiet => 1 });
|
|
|
|
}
|
2020-06-15 19:42:13 +00:00
|
|
|
|
2020-08-31 03:14:54 +00:00
|
|
|
sub CheckUpstreamAndPull {
|
|
|
|
# Returns true iff. a pull was required and the files have changed.
|
|
|
|
return 0 unless (defined $tempRepository);
|
|
|
|
my $options = { quiet => 1 };
|
|
|
|
if ($VERBOSE > 5) {
|
|
|
|
print STDERR "...clean & git pull...";
|
|
|
|
$options = {};
|
|
|
|
}
|
|
|
|
print STDERR "...check if upstream Git changed..." if $VERBOSE > 5;
|
|
|
|
my $updateOutput = $tempRepository->run(remote => 'update', $options);
|
|
|
|
print "$updateOutput" if defined $updateOutput and $updateOutput !~ /^\s*$/ and $VERBOSE > 5;
|
|
|
|
my $curRev = $tempRepository->run('rev-parse' => '@');
|
|
|
|
my $remoteRev = $tempRepository->run('rev-parse' => '@{u}');
|
|
|
|
my $baseRev = $tempRepository->run('merge-base' => '@', '@{u}');
|
|
|
|
print STDERR "...$curRev is current, $remoteRev is remote Rev $baseRev is base...\n" if $VERBOSE > 6;
|
|
|
|
if ($curRev eq $remoteRev) {
|
|
|
|
print STDERR "no change..." if $VERBOSE > 5;
|
|
|
|
return 0;
|
|
|
|
} elsif ($curRev eq $baseRev) {
|
|
|
|
$tempRepository->run(clean => '-fx', $options);
|
|
|
|
$tempRepository->run(reset => '--hard', $options);
|
|
|
|
$tempRepository->run(clean => '-fx', $options);
|
|
|
|
my $pullOutput = $tempRepository->run('pull');
|
|
|
|
print STDERR "\nPerformed pull since remote updated:\n $pullOutput\n" if ($VERBOSE > 0);
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
CleanupEvertything();
|
|
|
|
die("our local Git has $curRev, upstream is at $remoteRev, and the base is $baseRev " .
|
|
|
|
"so give up entirely on trying to make this work.");
|
2020-06-15 19:42:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
my $glue = 'BeAn';
|
|
|
|
my %options = (
|
|
|
|
create => 'yes',
|
|
|
|
exclusive => 0,
|
|
|
|
mode => 0600,
|
|
|
|
destroy => 'yes',
|
|
|
|
);
|
|
|
|
my %query;
|
|
|
|
tie %query, 'IPC::Shareable', $glue, { %options } or
|
|
|
|
die "server: tie failed for $glue\n";
|
|
|
|
|
|
|
|
%query = ();
|
|
|
|
|
|
|
|
my %beancountData;
|
|
|
|
tie %beancountData, 'IPC::Shareable', $glue, { %options } or
|
|
|
|
die "server: tie failed\n";
|
|
|
|
|
Switch from IPC::Run to Expect module; set PAGER to /usr/bin/cat
After some efforts to make this current incarnation of the daemon
work for situations with excessively long output, I discovered two
problems.
First, bean-query seems to always use a pager in interactive mode,
and it seems impossible to dissuade bean-query from this behavior
without an upstream change to Beancount. As such, my first effort
was to force the pager to be `/usr/bin/cat`, which I thought would be
sufficient to solve the problem entirely.
After that change coupled with much debugging with IPC::Run, I found
that detecting the beancount> prompt using a regular expression of
/^\s*beancount\s*\>\s*/m, or similar efforts with simply
/\s*beancount\s*/ or other versions like /\s*beancount\s*\>\s*$/ms
/\s*beancount\s*\>\s*$/s, simply would not detect the end of the
string, so the process hung on this line in the main loop:
pump $runningBeanQuery until $rbcOut =~ /^\s*beancount\s*\>\s*/m;
Ironically, when there is *not* copious output, that line worked just
fine. It was baffling.
I switched to Expect, and found the exact same behavior with:
$runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*')
with that line, it works fine in the StartRunningBeanQuery()
subroutine, but when used in the main loop, Expect never finds that
string. Again, I tried it with various regexes as specified above to
no avail.
*However*, merely changing the line to:
$runningBeanQuery->expect(undef, 'beancount>')
(i.e., not using the regex feature in the Expect library), fixed the
problem entirely.
My working theory is some complex interaction bug between the IO::Pty
module, regular expression matching, and output timing (since this
problem only shows up when there is copious output). I think in
particular the fact that bean-query creates a subprocess for the
`PAGER` command in the pty when the output is copious is somehow
related to the problem.
Regardless, this solution now works in all the cases where I'm using
the daemon, so I'm not investigating further.
2020-08-17 15:46:57 +00:00
|
|
|
my ($currentFormat, $runningBeanQuery);
|
2020-08-10 13:37:08 +00:00
|
|
|
|
|
|
|
sub StartRunningBeanQuery {
|
|
|
|
my($format) = @_;
|
|
|
|
$format = $currentFormat unless defined $format;
|
|
|
|
$currentFormat = $format;
|
|
|
|
|
2020-08-31 03:14:54 +00:00
|
|
|
if (defined $tempRepository) {
|
|
|
|
print STDERR "Clearing temp files from repository..." if $VERBOSE > 4;
|
|
|
|
$tempRepository->run(clean => '-fx', { quiet => 1 });
|
|
|
|
$tempRepository->run(reset => '--hard', { quiet => 1 });
|
|
|
|
$tempRepository->run(clean => '-fx', { quiet => 1 });
|
|
|
|
} else {
|
|
|
|
my(@findCmd) = ("/usr/bin/find", '.', '-name', '*.picklecache');
|
|
|
|
if ($VERBOSE > 4) {
|
|
|
|
print STDERR "Cleared the following picklecache files (none listed means none existed)...\n";
|
|
|
|
push(@findCmd, '-ls');
|
|
|
|
}
|
|
|
|
push(@findCmd, '-exec', '/usr/bin/rm', '-f', '{}', ';');
|
|
|
|
system(@findCmd);
|
|
|
|
print STDERR "...done clearing picklecache files.\n" if ($VERBOSE > 4);
|
|
|
|
}
|
2020-08-10 13:37:08 +00:00
|
|
|
my @cmd = ($BEANCOUNT_QUERY_CMD);
|
2020-08-10 14:46:29 +00:00
|
|
|
push(@cmd, '-f', $format) if defined $format;
|
2020-08-10 13:37:08 +00:00
|
|
|
push(@cmd, $LOAD_FILE);
|
2020-08-31 03:14:54 +00:00
|
|
|
print STDERR "Starting beancount..." if $VERBOSE > 4;
|
Switch from IPC::Run to Expect module; set PAGER to /usr/bin/cat
After some efforts to make this current incarnation of the daemon
work for situations with excessively long output, I discovered two
problems.
First, bean-query seems to always use a pager in interactive mode,
and it seems impossible to dissuade bean-query from this behavior
without an upstream change to Beancount. As such, my first effort
was to force the pager to be `/usr/bin/cat`, which I thought would be
sufficient to solve the problem entirely.
After that change coupled with much debugging with IPC::Run, I found
that detecting the beancount> prompt using a regular expression of
/^\s*beancount\s*\>\s*/m, or similar efforts with simply
/\s*beancount\s*/ or other versions like /\s*beancount\s*\>\s*$/ms
/\s*beancount\s*\>\s*$/s, simply would not detect the end of the
string, so the process hung on this line in the main loop:
pump $runningBeanQuery until $rbcOut =~ /^\s*beancount\s*\>\s*/m;
Ironically, when there is *not* copious output, that line worked just
fine. It was baffling.
I switched to Expect, and found the exact same behavior with:
$runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*')
with that line, it works fine in the StartRunningBeanQuery()
subroutine, but when used in the main loop, Expect never finds that
string. Again, I tried it with various regexes as specified above to
no avail.
*However*, merely changing the line to:
$runningBeanQuery->expect(undef, 'beancount>')
(i.e., not using the regex feature in the Expect library), fixed the
problem entirely.
My working theory is some complex interaction bug between the IO::Pty
module, regular expression matching, and output timing (since this
problem only shows up when there is copious output). I think in
particular the fact that bean-query creates a subprocess for the
`PAGER` command in the pty when the output is copious is somehow
related to the problem.
Regardless, this solution now works in all the cases where I'm using
the daemon, so I'm not investigating further.
2020-08-17 15:46:57 +00:00
|
|
|
$runningBeanQuery = Expect->spawn(@cmd);
|
2020-08-31 03:14:54 +00:00
|
|
|
print STDERR "...spawned & loading data..." if $VERBOSE > 4;
|
Switch from IPC::Run to Expect module; set PAGER to /usr/bin/cat
After some efforts to make this current incarnation of the daemon
work for situations with excessively long output, I discovered two
problems.
First, bean-query seems to always use a pager in interactive mode,
and it seems impossible to dissuade bean-query from this behavior
without an upstream change to Beancount. As such, my first effort
was to force the pager to be `/usr/bin/cat`, which I thought would be
sufficient to solve the problem entirely.
After that change coupled with much debugging with IPC::Run, I found
that detecting the beancount> prompt using a regular expression of
/^\s*beancount\s*\>\s*/m, or similar efforts with simply
/\s*beancount\s*/ or other versions like /\s*beancount\s*\>\s*$/ms
/\s*beancount\s*\>\s*$/s, simply would not detect the end of the
string, so the process hung on this line in the main loop:
pump $runningBeanQuery until $rbcOut =~ /^\s*beancount\s*\>\s*/m;
Ironically, when there is *not* copious output, that line worked just
fine. It was baffling.
I switched to Expect, and found the exact same behavior with:
$runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*')
with that line, it works fine in the StartRunningBeanQuery()
subroutine, but when used in the main loop, Expect never finds that
string. Again, I tried it with various regexes as specified above to
no avail.
*However*, merely changing the line to:
$runningBeanQuery->expect(undef, 'beancount>')
(i.e., not using the regex feature in the Expect library), fixed the
problem entirely.
My working theory is some complex interaction bug between the IO::Pty
module, regular expression matching, and output timing (since this
problem only shows up when there is copious output). I think in
particular the fact that bean-query creates a subprocess for the
`PAGER` command in the pty when the output is copious is somehow
related to the problem.
Regardless, this solution now works in all the cases where I'm using
the daemon, so I'm not investigating further.
2020-08-17 15:46:57 +00:00
|
|
|
$runningBeanQuery->log_stdout(0);
|
|
|
|
$runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*')
|
|
|
|
or die("Unable to find beancount prompt, output was instead: ".
|
|
|
|
$runningBeanQuery->before() . $runningBeanQuery->after());
|
2020-08-31 03:14:54 +00:00
|
|
|
print STDERR "now ready." if $VERBOSE > 4;
|
Switch from IPC::Run to Expect module; set PAGER to /usr/bin/cat
After some efforts to make this current incarnation of the daemon
work for situations with excessively long output, I discovered two
problems.
First, bean-query seems to always use a pager in interactive mode,
and it seems impossible to dissuade bean-query from this behavior
without an upstream change to Beancount. As such, my first effort
was to force the pager to be `/usr/bin/cat`, which I thought would be
sufficient to solve the problem entirely.
After that change coupled with much debugging with IPC::Run, I found
that detecting the beancount> prompt using a regular expression of
/^\s*beancount\s*\>\s*/m, or similar efforts with simply
/\s*beancount\s*/ or other versions like /\s*beancount\s*\>\s*$/ms
/\s*beancount\s*\>\s*$/s, simply would not detect the end of the
string, so the process hung on this line in the main loop:
pump $runningBeanQuery until $rbcOut =~ /^\s*beancount\s*\>\s*/m;
Ironically, when there is *not* copious output, that line worked just
fine. It was baffling.
I switched to Expect, and found the exact same behavior with:
$runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*')
with that line, it works fine in the StartRunningBeanQuery()
subroutine, but when used in the main loop, Expect never finds that
string. Again, I tried it with various regexes as specified above to
no avail.
*However*, merely changing the line to:
$runningBeanQuery->expect(undef, 'beancount>')
(i.e., not using the regex feature in the Expect library), fixed the
problem entirely.
My working theory is some complex interaction bug between the IO::Pty
module, regular expression matching, and output timing (since this
problem only shows up when there is copious output). I think in
particular the fact that bean-query creates a subprocess for the
`PAGER` command in the pty when the output is copious is somehow
related to the problem.
Regardless, this solution now works in all the cases where I'm using
the daemon, so I'm not investigating further.
2020-08-17 15:46:57 +00:00
|
|
|
print STDERR "Beancount started with output of:\n", $runningBeanQuery->before(),
|
|
|
|
$runningBeanQuery->match(), $runningBeanQuery->after(), "\n"
|
|
|
|
if ($VERBOSE > 3);
|
2020-08-10 13:37:08 +00:00
|
|
|
}
|
2020-08-31 03:14:54 +00:00
|
|
|
sub StopRunningBeanQuery {
|
|
|
|
return if not defined $runningBeanQuery;
|
|
|
|
$runningBeanQuery->send("exit\n");
|
|
|
|
$runningBeanQuery->soft_close();
|
|
|
|
}
|
|
|
|
CheckUpstreamAndPull();
|
2020-08-10 13:37:08 +00:00
|
|
|
StartRunningBeanQuery('text');
|
2020-08-17 18:45:37 +00:00
|
|
|
print STDERR "Beancount started. Main loop begins." if $VERBOSE > 0;
|
2020-06-15 19:42:13 +00:00
|
|
|
while (1) {
|
|
|
|
if (not defined $query{question}) {
|
2020-08-31 03:14:54 +00:00
|
|
|
print STDERR "No question posed..." if $VERBOSE > 5;
|
|
|
|
if (CheckUpstreamAndPull()) {
|
|
|
|
StopRunningBeanQuery();
|
|
|
|
StartRunningBeanQuery();
|
|
|
|
}
|
2020-06-15 19:42:13 +00:00
|
|
|
if (defined $query{fifoName}) {
|
2020-08-31 03:14:54 +00:00
|
|
|
print STDERR "fifo still active; locking to clear it..." if $VERBOSE > 5;
|
2020-06-15 21:42:33 +00:00
|
|
|
(tied %query)->shlock;
|
2020-08-31 03:14:54 +00:00
|
|
|
print STDERR "clearing fifo, $query{fifoName}..." if $VERBOSE > 5;
|
2020-06-15 19:42:13 +00:00
|
|
|
no autodie 'unlink'; unlink($query{fifoName});
|
2020-06-16 03:40:22 +00:00
|
|
|
%query = ();
|
2020-06-15 21:42:33 +00:00
|
|
|
(tied %query)->shunlock;
|
2020-08-31 03:14:54 +00:00
|
|
|
print STDERR "fifo cleared & lock released." if $VERBOSE > 5;
|
2020-06-15 19:42:13 +00:00
|
|
|
}
|
2020-08-31 03:14:54 +00:00
|
|
|
print STDERR "sleep for 2 seconds\n" if $VERBOSE > 5;
|
2020-06-15 19:42:13 +00:00
|
|
|
sleep 2;
|
|
|
|
next;
|
2020-09-23 23:45:01 +00:00
|
|
|
# } elsif ($query{question} !~ /^[\,\=\~\-\@\w.\s\"\'\_\(\)\<\>\*\.\!\^\:\$\|]+$/) {
|
|
|
|
} elsif ($query{question} !~ /^[\s\S]+$/) {
|
2020-06-15 19:42:13 +00:00
|
|
|
print STDERR "Query string $query{question} looks suspicious, not running beancount query!\n";
|
|
|
|
(tied %query)->shlock;
|
2020-06-16 03:37:49 +00:00
|
|
|
$query{fifoName} = mktemp("REJECTED_beancount-query-fifo-this-file-does-not-exist_${$}_XXXXXXXXX");
|
|
|
|
(tied %query)->shunlock;
|
2020-06-16 03:40:22 +00:00
|
|
|
sleep 2;
|
2020-06-16 03:37:49 +00:00
|
|
|
} elsif (defined $query{format} and $query{format} !~ /^(?:csv|text)$/) {
|
|
|
|
print STDERR "format string $query{format} is not text or csv, not running beancount query!\n";
|
|
|
|
(tied %query)->shlock;
|
|
|
|
$query{question} = $query{format} = undef;
|
2020-06-15 19:42:13 +00:00
|
|
|
$query{fifoName} = mktemp("REJECTED_beancount-query-fifo-this-file-does-not-exist_${$}_XXXXXXXXX");
|
|
|
|
(tied %query)->shunlock;
|
|
|
|
} elsif (not defined $query{fifoName}) {
|
2020-09-25 17:49:34 +00:00
|
|
|
if (defined $query{format}) {
|
|
|
|
unless ($query{format} =~ /^(?:text|csv)$/) {
|
|
|
|
print STDERR "format string $query{format} is not supported yet!\n";
|
|
|
|
(tied %query)->shlock;
|
|
|
|
$query{question} = $query{format} = undef;
|
|
|
|
$query{fifoName} = mktemp("REJECTED_beancount-$query{format}_not_supported_${$}_XXXXXXXXX");
|
|
|
|
(tied %query)->shunlock;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
if ($currentFormat ne $query{format}) {
|
|
|
|
$runningBeanQuery->send("set format $query{format}\n");
|
|
|
|
$runningBeanQuery->expect(undef, 'beancount>') # *Don't* use regex here!
|
|
|
|
or die("Unable to find beancount prompt, output was instead: ".
|
|
|
|
$runningBeanQuery->before() . $runningBeanQuery->after());
|
|
|
|
$currentFormat = $query{format};
|
|
|
|
print STDERR "Switched formats to the $currentFormat..." if $VERBOSE > 3;
|
|
|
|
my $rbcBefore = $runningBeanQuery->before();
|
|
|
|
die("Unable to change format to $currentFormat")
|
|
|
|
unless $rbcBefore =~ /format\s*:\s*$currentFormat/ixm;
|
|
|
|
}
|
2020-08-10 14:35:32 +00:00
|
|
|
}
|
2020-06-16 03:40:22 +00:00
|
|
|
print STDERR "Runing query: $query{question}\n" if $VERBOSE > 0;
|
2020-08-10 14:35:32 +00:00
|
|
|
my $ques = $query{question};
|
|
|
|
$ques =~ s/\n/ /gm;
|
Switch from IPC::Run to Expect module; set PAGER to /usr/bin/cat
After some efforts to make this current incarnation of the daemon
work for situations with excessively long output, I discovered two
problems.
First, bean-query seems to always use a pager in interactive mode,
and it seems impossible to dissuade bean-query from this behavior
without an upstream change to Beancount. As such, my first effort
was to force the pager to be `/usr/bin/cat`, which I thought would be
sufficient to solve the problem entirely.
After that change coupled with much debugging with IPC::Run, I found
that detecting the beancount> prompt using a regular expression of
/^\s*beancount\s*\>\s*/m, or similar efforts with simply
/\s*beancount\s*/ or other versions like /\s*beancount\s*\>\s*$/ms
/\s*beancount\s*\>\s*$/s, simply would not detect the end of the
string, so the process hung on this line in the main loop:
pump $runningBeanQuery until $rbcOut =~ /^\s*beancount\s*\>\s*/m;
Ironically, when there is *not* copious output, that line worked just
fine. It was baffling.
I switched to Expect, and found the exact same behavior with:
$runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*')
with that line, it works fine in the StartRunningBeanQuery()
subroutine, but when used in the main loop, Expect never finds that
string. Again, I tried it with various regexes as specified above to
no avail.
*However*, merely changing the line to:
$runningBeanQuery->expect(undef, 'beancount>')
(i.e., not using the regex feature in the Expect library), fixed the
problem entirely.
My working theory is some complex interaction bug between the IO::Pty
module, regular expression matching, and output timing (since this
problem only shows up when there is copious output). I think in
particular the fact that bean-query creates a subprocess for the
`PAGER` command in the pty when the output is copious is somehow
related to the problem.
Regardless, this solution now works in all the cases where I'm using
the daemon, so I'm not investigating further.
2020-08-17 15:46:57 +00:00
|
|
|
$runningBeanQuery->send("$ques\n");
|
|
|
|
$runningBeanQuery->expect(undef, 'beancount>') # *Don't* use regex here!
|
|
|
|
# See `git annotate` on this line for why no regex.
|
|
|
|
or die("Unable to find beancount prompt, output was instead: ".
|
|
|
|
$runningBeanQuery->before() . $runningBeanQuery->after());
|
2020-06-15 19:42:13 +00:00
|
|
|
(tied %query)->shlock;
|
2020-08-31 03:12:20 +00:00
|
|
|
print STDERR "Acquired shlock on tied variable.\n" if $VERBOSE > 2;
|
2020-06-15 19:42:13 +00:00
|
|
|
$query{fifoName} = undef;
|
|
|
|
my $cnt = 0;
|
|
|
|
my $fifoFileName = mktemp(catfile($FIFO_DIR, "beancount-query-fifo_${$}_XXXXXXXXX"));
|
|
|
|
mkfifo($fifoFileName, 0700);
|
|
|
|
die "unable to create fifo in $fifoFileName: $!" unless -p $fifoFileName;
|
2020-08-31 03:12:20 +00:00
|
|
|
print STDERR "Created $fifoFileName..." if $VERBOSE > 2;
|
2020-06-15 19:42:13 +00:00
|
|
|
$query{fifoName} = $fifoFileName;
|
2020-08-31 03:12:20 +00:00
|
|
|
my $getAllOutput = $query{getAllOutput};
|
2020-06-15 19:42:13 +00:00
|
|
|
(tied %query)->shunlock;
|
2020-09-23 23:46:11 +00:00
|
|
|
print STDERR "unlocked tied query..." if $VERBOSE > 2;
|
2020-06-15 19:42:13 +00:00
|
|
|
open(my $fifoFH, ">", $fifoFileName);
|
2020-09-23 23:46:11 +00:00
|
|
|
print STDERR "and began write to fifo." if $VERBOSE > 2;
|
2020-08-31 03:12:20 +00:00
|
|
|
my($seenSeperator, $prevLine) = ( ((defined $getAllOutput) and $getAllOutput), "");
|
Switch from IPC::Run to Expect module; set PAGER to /usr/bin/cat
After some efforts to make this current incarnation of the daemon
work for situations with excessively long output, I discovered two
problems.
First, bean-query seems to always use a pager in interactive mode,
and it seems impossible to dissuade bean-query from this behavior
without an upstream change to Beancount. As such, my first effort
was to force the pager to be `/usr/bin/cat`, which I thought would be
sufficient to solve the problem entirely.
After that change coupled with much debugging with IPC::Run, I found
that detecting the beancount> prompt using a regular expression of
/^\s*beancount\s*\>\s*/m, or similar efforts with simply
/\s*beancount\s*/ or other versions like /\s*beancount\s*\>\s*$/ms
/\s*beancount\s*\>\s*$/s, simply would not detect the end of the
string, so the process hung on this line in the main loop:
pump $runningBeanQuery until $rbcOut =~ /^\s*beancount\s*\>\s*/m;
Ironically, when there is *not* copious output, that line worked just
fine. It was baffling.
I switched to Expect, and found the exact same behavior with:
$runningBeanQuery->expect(undef, -re => '^\s*beancount\s*\>\s*')
with that line, it works fine in the StartRunningBeanQuery()
subroutine, but when used in the main loop, Expect never finds that
string. Again, I tried it with various regexes as specified above to
no avail.
*However*, merely changing the line to:
$runningBeanQuery->expect(undef, 'beancount>')
(i.e., not using the regex feature in the Expect library), fixed the
problem entirely.
My working theory is some complex interaction bug between the IO::Pty
module, regular expression matching, and output timing (since this
problem only shows up when there is copious output). I think in
particular the fact that bean-query creates a subprocess for the
`PAGER` command in the pty when the output is copious is somehow
related to the problem.
Regardless, this solution now works in all the cases where I'm using
the daemon, so I'm not investigating further.
2020-08-17 15:46:57 +00:00
|
|
|
my $rbcOut = $runningBeanQuery->before();
|
2020-08-10 13:37:08 +00:00
|
|
|
foreach my $line (split /\n/, $rbcOut) {
|
2020-08-10 14:35:32 +00:00
|
|
|
# Occasionally, some of the SELECT statement is printed back to
|
|
|
|
# $rbcOut. Avoid reproducing this in output by waiting for the line
|
|
|
|
# of ----'s. Thus, this only works in text mode right now.
|
|
|
|
unless ($seenSeperator) {
|
|
|
|
if ($line =~ /^\s*$/) {
|
|
|
|
$seenSeperator = 1;
|
2020-09-25 17:49:34 +00:00
|
|
|
} elsif (($currentFormat eq 'text' and $line =~ /^\s*[\-\s]+\s*$/)
|
|
|
|
or ($currentFormat eq 'csv' and $line =~ /^\s*(\S+,)*\S+\s*$/)){
|
2020-08-10 14:35:32 +00:00
|
|
|
$seenSeperator = 1;
|
|
|
|
}
|
|
|
|
$prevLine = $line;
|
|
|
|
next;
|
|
|
|
}
|
2020-08-10 13:37:08 +00:00
|
|
|
last if $line =~ /^\s*beancount\s*\>\s*/;
|
2020-06-15 21:52:23 +00:00
|
|
|
print STDERR "." unless ($cnt++ % 500) or ($VERBOSE <= 1);
|
2020-08-10 13:37:08 +00:00
|
|
|
print $fifoFH "$line\n";
|
2020-08-10 14:35:32 +00:00
|
|
|
$prevLine = $line;
|
2020-06-15 19:42:13 +00:00
|
|
|
}
|
|
|
|
close $fifoFH;
|
|
|
|
(tied %query)->shlock; $query{question} = undef; (tied %query)->shunlock;
|
|
|
|
print STDERR "...done! Data now in $fifoFileName\n" if $VERBOSE > 0;
|
|
|
|
}
|
|
|
|
}
|
2020-08-10 13:37:08 +00:00
|
|
|
finish $runningBeanQuery;
|
2020-06-15 19:42:13 +00:00
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
#
|
|
|
|
# Local variables:
|
|
|
|
# compile-command: "perl -c bean-query-goofy-daemon.plx"
|
|
|
|
# perl-indent-level: 2
|
|
|
|
# End:
|
|
|
|
|