File Coverage

blib/lib/DBIx/FileSystem.pm
Criterion Covered Total %
statement 75 1390 5.4
branch 16 668 2.4
condition 5 158 3.1
subroutine 16 52 30.7
pod 0 38 0.0
total 112 2306 4.8


line stmt bran cond sub pod time code
1             #
2             # DBIx::FileSystem;
3             #
4             # Manage database tables with a simulated filesystem shell environment
5             #
6             # Mar 2003,2018 Alexander Haderer
7             #
8             # License:
9             #
10             # This program is free software; you can redistribute it and/or modify
11             # it under the terms of the GNU General Public License as published by
12             # the Free Software Foundation; either version 2 of the License, or
13             # (at your option) any later version.
14             #
15             # This program is distributed in the hope that it will be useful,
16             # but WITHOUT ANY WARRANTY; without even the implied warranty of
17             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18             # GNU General Public License for more details.
19             #
20             # You should have received a copy of the GNU General Public License
21             # along with this program; if not, write to the Free Software
22             # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23             #
24              
25             package DBIx::FileSystem;
26              
27 4     4   229898 use strict;
  4         46  
  4         151  
28 4     4   24 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS );
  4         8  
  4         353  
29 4     4   29 use Exporter;
  4         8  
  4         190  
30              
31             # for access class: return results
32 4     4   28 use constant OK => 0; # everything ok
  4         8  
  4         416  
33 4     4   25 use constant NOFILE => 1; # file not found in db
  4         8  
  4         211  
34 4     4   26 use constant NFOUND => 2; # more than one entry found
  4         8  
  4         189  
35 4     4   22 use constant ERROR => 3; # nothing found, errorstring set
  4         6  
  4         428  
36              
37             $DBIx::FileSystem::VERSION = '2.5';
38              
39             @ISA = qw( Exporter );
40             @EXPORT = qw( );
41             @EXPORT_OK = qw(
42             &recreatedb
43             &mainloop
44             OK
45             NOFILE
46             NFOUND
47             ERROR
48             );
49             %EXPORT_TAGS = ( symbols => [ qw( OK NOFILE NFOUND ERROR ) ] );
50              
51 4     4   27 use vars qw( $OUT $vwd $dbh );
  4         7  
  4         193  
52              
53 4     4   4504 use DBI;
  4         59584  
  4         271  
54 4     4   2559 use Term::ReadLine;
  4         11327  
  4         142  
55 4     4   3061 use File::Temp ();
  4         89455  
  4         131  
56 4     4   1554 use POSIX;
  4         18656  
  4         28  
57              
58 4     4   9246 use Fcntl;
  4         10  
  4         56168  
59              
60             ########################################################################
61             ########################################################################
62             ## classical interface: the shell
63             ########################################################################
64             ########################################################################
65              
66              
67              
68             ########################################################################
69             # c o m m a n d s
70             ########################################################################
71             my %commands =
72             ('cd'=> { func => \&com_cd,
73             doc => "change to directory: 'cd DIR'" },
74             'help' => { func => \&com_help,
75             doc => "display help text: 'help [command]'" },
76             'quit' => { func => \&com_quit,
77             doc => "quit it" },
78             'ls' => { func => \&com_ls,
79             doc => "list dirs and files" },
80             'ld'=> { func => \&com_ld,
81             doc => "list long dirs and files with comments" },
82             'll' => { func => \&com_ll,
83             doc => "list long files with comments" },
84             'rm' => { func => \&com_rm,
85             doc => "remove file: 'rm FILE'" },
86             'cp' => { func => \&com_cp,
87             doc => "copy file: 'cp OLD NEW'" },
88             'cat' => { func => \&com_cat,
89             doc => "show contents of a file: 'cat FILE'" },
90             'sum' => { func => \&com_sum,
91             doc => "show summary of a file: 'sum FILE'" },
92             'vi' => { func => \&com_vi,
93             doc => "edit/create a file: 'vi FILE'" },
94             'ver' => { func => \&com_ver,
95             doc => "show version" },
96             'vgrep' => { func => \&com_vgrep,
97             doc => "grep var/value pairs in all files: vgrep PATTERN" },
98             'wrefs' => { func => \&com_wref,
99             doc => "show who references a file: 'wrefs FILE'" },
100             );
101              
102              
103             ########################################################################
104             # C o n s t a n t s
105             ########################################################################
106              
107             # for ls output
108             my $NUM_LS_COL = 4;
109             my $LS_COL_WIDTH = 16;
110             my $EDITOR = $ENV{EDITOR};
111             $EDITOR = "/usr/bin/vi" unless $EDITOR;
112              
113              
114             ########################################################################
115             # m a i n
116             #
117             # input:
118             # vdirs: reference to vdir hash
119             # PRG: program name for the shell-program
120             # VERSION four digit version string for program/database version
121             # DBCONN DBI connect string for the database
122             # DBUSER database user
123             #
124             # returns nothing
125             ########################################################################
126              
127             my $vdirs; # reference to vdir hash
128             # $vwd ; # current virtual working directory (exported)
129              
130             # my $dbh; # database handle (exported)
131             my $term;
132             # $OUT; # the stdout (exported)
133              
134             my $DBCONN; # DBI database connect string
135             my $DBUSER; # DBI database user
136             my $DBPWD; # DBI password
137             my $VERSION;
138              
139             my $PRG; # program name of the shell
140              
141              
142             sub mainloop(\%$$$$$\%) {
143              
144 0     0 0 0 my $customcmds;
145 0         0 ($vdirs,$PRG,$VERSION,$DBCONN,$DBUSER,$DBPWD,$customcmds) = @_;
146              
147             # merge custom commands, if any
148 0 0       0 if( defined $customcmds ) {
149 0         0 foreach my $cucmd (keys (%{$customcmds} ) ) {
  0         0  
150 0 0       0 if( defined $commands{$cucmd} ) {
151 0         0 die "$PRG: redefinition of command '$cucmd' by customcommands";
152             }
153 0 0       0 unless( defined $customcmds->{$cucmd}{func} ) {
154 0         0 die "$PRG: customcommand '$cucmd': elem func not set";
155             }
156 0 0       0 unless( defined $customcmds->{$cucmd}{doc} ) {
157 0         0 die "$PRG: customcommand '$cucmd': elem doc not set";
158             }
159 0         0 $commands{$cucmd} = $customcmds->{$cucmd};
160             }
161             }
162              
163             # connect to db
164 0 0       0 ($dbh = DBI->connect( $DBCONN, $DBUSER, $DBPWD,
165             {ChopBlanks => 1, AutoCommit => 1, PrintError => 0}))
166             || die "$PRG: connect to '$DBCONN' failed:\n", $DBI::errstr;
167              
168             # check vdirs
169 0 0       0 if( check_vdirs_struct() ) {
170 0 0       0 $dbh->disconnect || die "$PRG: Disconnect failed. Reason: ", $DBI::errstr;
171 0         0 die "$PRG: check 'vdirs' structure in $PRG\n";
172             }
173              
174             # check database
175 0 0       0 if( check_db_tables() ) {
176 0 0       0 $dbh->disconnect || die "$PRG: Disconnect failed. Reason: ", $DBI::errstr;
177 0         0 die "$PRG: database wrong: run '$PRG recreatedb' to recreate tables\n";
178             }
179              
180             # readline settings
181 0         0 $term = new Term::ReadLine 'dbshell console';
182 0   0     0 $OUT = $term->OUT || \*STDOUT;
183 0         0 $term->ornaments( 0 );
184 0         0 $term->Attribs->{attempted_completion_function} = \&dbshell_completion;
185              
186 0         0 my $line; # command line
187             my $cmd; # the command
188 0         0 my @arg; # the command's parameters
189              
190 0         0 my $prompttemplate = "$PRG (/%s): ";
191 0         0 my $prompt = sprintf( $prompttemplate, $vwd );
192              
193             # the loop
194 0         0 while ( defined ($line = $term->readline($prompt)) ) {
195             # remove whitespace
196 0         0 $line =~ s/^\s*//;
197 0         0 $line =~ s/\s*//;
198 0         0 ($cmd, @arg ) = split( ' ', $line );
199 0 0       0 next unless defined $cmd;
200            
201 0         0 my $command = $commands{$cmd};
202 0 0       0 if( defined $command ) {
203 0 0       0 last if &{$command->{func}}( @arg );
  0         0  
204             }else{
205 0         0 print $OUT "unknown command '$cmd', try 'help'\n";
206             }
207 0         0 $prompt = sprintf( $prompttemplate, $vwd );
208             }
209 0 0       0 $dbh->disconnect || die "$PRG: Disconnect failed. Reason: ", $DBI::errstr;
210 0         0 return;
211             }
212              
213              
214             sub recreatedb(\%$$$$$) {
215              
216 0     0 0 0 ($vdirs,$PRG,$VERSION,$DBCONN,$DBUSER,$DBPWD) = @_;
217              
218             # connect to db
219 0 0       0 ($dbh = DBI->connect( $DBCONN, $DBUSER, $DBPWD,
220             {ChopBlanks => 1, AutoCommit => 1, PrintError => 0}))
221             || die "$PRG: connect to '$DBCONN' failed:\n", $DBI::errstr;
222              
223             # check vdirs
224 0 0       0 if( check_vdirs_struct() ) {
225 0         0 die "$PRG: check 'vdirs' structure in $PRG\n";
226             }
227              
228 0         0 recreate_db_tables();
229              
230 0 0       0 $dbh->disconnect || die "$PRG: Disconnect failed. Reason: ", $DBI::errstr;
231 0         0 return;
232             }
233              
234              
235             ########################################################################
236             # c o m m a n d f u n c t i o n s
237             ########################################################################
238              
239             ########################################################################
240             # com_help()
241             #
242             sub com_help() {
243 0     0 0 0 my $arg = shift;
244 0 0       0 if( defined $arg ) {
245 0 0       0 if( defined $commands{$arg} ) {
246 0         0 print $OUT "$arg\t$commands{$arg}->{doc}\n";
247             }else{
248 0         0 print $OUT "no help for '$arg'\n";
249             }
250             }else{
251 0         0 foreach my $i (sort keys(%commands) ) {
252 0         0 print $OUT "$i\t$commands{$i}->{doc}\n";
253             }
254             }
255 0         0 return 0;
256             }
257              
258             ########################################################################
259             # com_ls()
260             #
261             sub com_ls() {
262 0     0 0 0 my @files;
263             my $i;
264              
265 0         0 my $x = shift;
266 0 0       0 if( defined $x ) {
267 0         0 print $OUT "ls: usage: $commands{ls}->{doc}\n";
268 0         0 return 0;
269             }
270              
271             # get dirs
272 0         0 foreach $i (sort keys(%{$vdirs}) ) {
  0         0  
273 0         0 push @files, "($i)";
274             }
275              
276             # get files
277 0 0       0 if( length($vwd) ) {
278 0         0 my $st;
279 0         0 my $col = $vdirs->{$vwd}{fnamcol};
280 0         0 $st = $dbh->prepare("select $col from $vwd order by $col");
281 0 0       0 unless( $st ) {
282 0         0 print $OUT "$PRG: can't prepare ls query '$vwd':\n " . $dbh->errstr;
283 0         0 return 0;
284             }
285 0 0       0 unless( $st->execute() ) {
286 0         0 print $OUT "$PRG: can't exec ls query '$vwd':\n " . $dbh->errstr;
287 0         0 return 0;
288             }
289 0         0 while( $i = $st->fetchrow_array() ) {
290 0         0 push @files, "$i";
291             }
292 0         0 $st->finish();
293             }
294              
295             # show it
296 0         0 my $numrow = int( $#files / $NUM_LS_COL ) + 1;
297 0         0 my $r = 0;
298 0         0 my $c = 0;
299 0         0 my $placeh = $LS_COL_WIDTH - 2;
300 0         0 for( $r=0; $r<$numrow; $r++ ) {
301 0         0 for( $c=0; $c<$NUM_LS_COL; $c++ ) {
302 0         0 $i = $c*$numrow+$r;
303 0 0       0 printf $OUT "%-${placeh}s ", $files[$i] if $i <= $#files;
304             }
305 0         0 print $OUT "\n";
306             }
307 0         0 return 0;
308             }
309              
310             ########################################################################
311             # com_ld()
312             #
313             sub com_ld() {
314 0     0 0 0 my @files;
315             my @com; # comments
316 0         0 my $i;
317 0         0 my $x = shift;
318 0 0       0 if( defined $x ) {
319 0         0 print $OUT "ls: usage: $commands{ld}->{doc}\n";
320 0         0 return 0;
321             }
322              
323             # get dirs
324 0         0 foreach $i (sort keys(%{$vdirs}) ) {
  0         0  
325 0         0 push @files, "($i)";
326 0         0 push @com, $vdirs->{$i}{desc};
327             }
328              
329             # show it
330 0         0 my $maxlen = 0;
331 0         0 foreach $i (@files) {
332 0 0       0 if( length($i) > $maxlen ) {$maxlen = length($i); }
  0         0  
333             }
334              
335 0         0 for( $i=0; $i<=$#files; $i++ ) {
336 0         0 printf $OUT "%-${maxlen}s| %s\n", $files[$i], $com[$i];
337             }
338 0         0 print $OUT "\n";
339 0         0 com_ll();
340 0         0 return 0;
341             }
342              
343             ########################################################################
344             # com_ll()
345             #
346             sub com_ll() {
347 0     0 0 0 my @files;
348             my @com; # comments
349 0         0 my $i;
350 0         0 my $c;
351              
352 0         0 my $x = shift;
353 0 0       0 if( defined $x ) {
354 0         0 print $OUT "ls: usage: $commands{ll}->{doc}\n";
355 0         0 return 0;
356             }
357              
358             # get files
359 0 0       0 if( defined $vdirs->{$vwd}{comcol} ) {
360 0         0 my $comcol = $vdirs->{$vwd}{comcol};
361 0         0 my $col = $vdirs->{$vwd}{fnamcol};
362 0         0 my $st;
363 0         0 $st = $dbh->prepare("select $col, $comcol from $vwd order by $col");
364 0 0       0 unless( $st ) {
365 0         0 print $OUT "$PRG: can't prepare ll query '$vwd':\n " . $dbh->errstr;
366 0         0 return 0;
367             }
368 0 0       0 unless( $st->execute() ) {
369 0         0 print $OUT "$PRG: can't exec ll query '$vwd':\n " . $dbh->errstr;
370 0         0 return 0;
371             }
372 0         0 while( ($i,$c) = $st->fetchrow_array() ) {
373 0 0       0 $c = "" unless defined $c;
374 0         0 push @files, "$i";
375 0         0 push @com, "$c";
376             }
377 0         0 $st->finish();
378             }else{
379 0         0 my $st;
380 0         0 my $col = $vdirs->{$vwd}{fnamcol};
381 0         0 $st = $dbh->prepare("select $col from $vwd order by $col");
382 0 0       0 unless( $st ) {
383 0         0 print $OUT "$PRG: can't prepare ls query '$vwd':\n " . $dbh->errstr;
384 0         0 return 0;
385             }
386 0 0       0 unless( $st->execute() ) {
387 0         0 print $OUT "$PRG: can't exec ls query '$vwd':\n " . $dbh->errstr;
388 0         0 return 0;
389             }
390 0         0 while( $i = $st->fetchrow_array() ) {
391 0         0 push @files, "$i";
392 0         0 push @com, "";
393             }
394 0         0 $st->finish();
395             }
396              
397             # show it
398 0         0 my $maxlen = 0;
399 0         0 foreach $i (@files) {
400 0 0       0 if( length($i) > $maxlen ) {$maxlen = length($i); }
  0         0  
401             }
402              
403 0         0 for( $i=0; $i<=$#files; $i++ ) {
404 0         0 printf $OUT "%-${maxlen}s| %s\n", $files[$i], $com[$i];
405             }
406 0         0 return 0;
407             }
408              
409             ########################################################################
410             # com_cd()
411             #
412             sub com_cd() {
413 0     0 0 0 my ($arg,$x) = @_;
414 0 0 0     0 if( defined $arg and !defined $x) {
415 0 0       0 if( exists $vdirs->{$arg} ) {
416 0         0 $vwd = "$arg";
417             }else{
418 0         0 print $OUT "no such directory '$arg'\n";
419             }
420             }else{
421 0         0 print $OUT "cd: usage: $commands{cd}->{doc}\n";
422             }
423 0         0 return 0;
424             }
425              
426              
427             ########################################################################
428             # com_quit()
429             #
430             sub com_quit() {
431 0     0 0 0 return 1;
432             }
433              
434             ########################################################################
435             # com_ver()
436             #
437             sub com_ver() {
438 0     0 0 0 print $OUT " DBIx-FileSystem Version: $DBIx::FileSystem::VERSION\n";
439 0         0 print $OUT " $PRG \%vdirs version: $VERSION\n";
440 0         0 return 0;
441             }
442              
443             ########################################################################
444             # com_rm()
445             #
446             sub com_rm() {
447 0     0 0 0 my $r;
448 0         0 my ($arg,$x) = @_;
449 0 0 0     0 if( defined $arg and !defined $x ) {
450 0 0       0 if( $vdirs->{$vwd}{edit} ) {
451 0 0 0     0 if( $vdirs->{$vwd}{defaultfile} and $vdirs->{$vwd}{defaultfile} eq $arg ) {
452 0         0 print $OUT "rm: error: cannot remove default file '$arg'\n";
453             }else{
454 0         0 my @reffiles = get_who_refs_me( $vwd, $arg );
455 0 0       0 if( $#reffiles == -1 ) {
456 0         0 my $rmerr;
457 0 0       0 if( exists $vdirs->{$vwd}{rmcheck} ) {
458 0         0 $rmerr = &{$vdirs->{$vwd}->{rmcheck}}( $vwd, $arg, $dbh);
  0         0  
459             }
460 0 0       0 if( defined $rmerr ) {
461 0         0 print $OUT "rm: cannot remove: $rmerr\n";
462             }else{
463 0         0 my $fnc = $vdirs->{$vwd}{fnamcol};
464 0         0 $r = $dbh->do( "delete from $vwd where $fnc='$arg'");
465 0 0       0 if( !defined $r ) {
    0          
466 0         0 print $OUT "rm: database error:\n" . $dbh->errstr;
467             }elsif( $r==0 ) {
468 0         0 print $OUT "rm: no such file '$arg'\n";
469             }
470             }
471             }else{
472 0         0 print $OUT "rm: cannot remove: file '$arg' referenced by:\n ";
473 0         0 print $OUT join( "\n ", @reffiles );
474 0         0 print $OUT "\n";
475             }
476             }
477             }else{
478 0         0 print $OUT "rm: error: read only directory '/$vwd'\n";
479             }
480             }else{
481 0         0 print $OUT "rm: usage: $commands{rm}{doc}\n";
482             }
483 0         0 return 0;
484             }
485              
486              
487             ########################################################################
488             # com_cp()
489             #
490             sub com_cp() {
491 0     0 0 0 my $r;
492 0         0 my ($old,$new,$x) = @_;
493 0 0 0     0 if( defined $old and defined $new and !defined $x) {
      0        
494 0 0       0 if( $vdirs->{$vwd}{edit} ) {
495 0         0 my $fnc = $vdirs->{$vwd}{fnamcol};
496 0 0 0     0 if( (length($new)<=$vdirs->{$vwd}{cols}{$fnc}{len}) and !($new=~/\W+/)) {
497 0         0 my $fnc = $vdirs->{$vwd}{fnamcol};
498 0         0 my $insert = "insert into $vwd (";
499 0         0 my $select = "select ";
500 0         0 my $cols = $vdirs->{$vwd}{cols};
501 0         0 foreach my $col (sort keys(%{$cols}) ) {
  0         0  
502 0         0 $insert .= "$col,";
503 0 0       0 if( $col eq $fnc ) {
    0          
    0          
504 0         0 $select .= "'$new',";
505             }elsif( exists $vdirs->{$vwd}{cols}{$col}{uniq} ) {
506 0         0 $select .= "NULL,";
507             }elsif( exists $vdirs->{$vwd}{cols}{$col}{cpdelete} ) {
508 0         0 $select .= "NULL,";
509             }else{
510 0         0 $select .= "$col,";
511             }
512             }
513 0         0 chop $insert;
514 0         0 chop $select;
515 0         0 $insert .= ")";
516 0         0 $select .= " from $vwd where $fnc='$old'";
517 0         0 $r = $dbh->do( "$insert $select");
518 0 0 0     0 if( !defined $r or $r!=1 ) {
519 0         0 print "cp: error: no file '$old' or file '$new' exists\n";
520             }
521             }else{
522 0         0 print $OUT "cp: error: illegal or to long filename '$new'\n";
523             }
524             }else{
525 0         0 print $OUT "cp: error: read only directory '/$vwd'\n";
526             }
527             }else{
528 0         0 print $OUT "cp: usage: $commands{cp}{doc}\n";
529             }
530 0         0 return 0;
531             }
532              
533              
534             ########################################################################
535             # com_sum()
536             #
537             sub com_sum() {
538 0     0 0 0 my ($arg,$x) = @_;
539 0 0 0     0 if( defined $arg and !defined $x ) {
540 0 0       0 if( print_file( $OUT, $arg, 0 ) == 1 ) {
541 0         0 print $OUT "sum: no such file '$arg'\n";
542             }
543             }else{
544 0         0 print $OUT "sum: usage: $commands{sum}{doc}\n";
545             }
546 0         0 return 0;
547             }
548              
549             ########################################################################
550             # com_cat()
551             #
552             sub com_cat() {
553 0     0 0 0 my ($arg,$x) = @_;
554              
555 0 0 0     0 if( defined $arg and !defined $x ) {
556 0 0       0 if( print_file( $OUT, $arg, 1 ) == 1 ) {
557 0         0 print $OUT "cat: no such file '$arg'\n";
558             }
559             }else{
560 0         0 print $OUT "cat: usage: $commands{cat}{doc}\n";
561             }
562 0         0 return 0;
563             }
564              
565             ########################################################################
566             # com_vi()
567             #
568             sub com_vi() {
569 0     0 0 0 my ($arg,$x) = @_;
570 0         0 my $tmpf;
571             my $tmpf_mtime;
572 0         0 my $r; # 0: file printed exists / create update SQL string
573             ; # 1: file printed did not exist / create insert SQL string
574 0         0 my $err;
575 0         0 my $sql;
576 0         0 my $ln = 1; # line number where editor starts
577              
578 0 0 0     0 if( defined $arg and !defined $x ) {
579 0 0       0 if( $vdirs->{$vwd}{edit} ) {
580 0         0 my $fnc = $vdirs->{$vwd}{fnamcol};
581 0 0 0     0 if( (length($arg)<=$vdirs->{$vwd}{cols}{$fnc}{len}) and !($arg=~/\W+/)) {
582 0         0 my $fh = File::Temp->new( UNLINK => 0,
583             TEMPLATE => "${arg}.XXXXXXXX",
584             TMPDIR => 1 );
585 0         0 $tmpf = $fh->filename();
586 0         0 $r = print_file( $fh, $arg, 2 );
587 0         0 close( $fh );
588 0         0 $tmpf_mtime = (stat $tmpf)[9]; # remember mtime of tempfile
589 0 0 0     0 if( $r==0 or $r==1 ) {
590 0         0 while( 1 ) {
591 0         0 system( "$EDITOR +$ln $tmpf" );
592 0         0 ($ln,$err,$sql) = create_sql_from_file( $tmpf, $vwd, $arg, $r );
593 0 0       0 if( defined $err ) {
594 0         0 my $inp = want_to_edit_again( $err );
595 0 0       0 next if $inp eq 'y';
596 0 0       0 last if $inp eq 'n';
597             }
598             ### print $OUT ">>>$sql<<<\n"; ######### hierhierhier
599 0 0 0     0 if( length($sql) and $tmpf_mtime != (stat $tmpf)[9] ) {
600 0         0 my $res = $dbh->do( $sql );
601 0 0       0 if( !defined $res ) {
    0          
602 0         0 my $inp=want_to_edit_again( "save to database:\n".$dbh->errstr);
603 0 0       0 if($inp eq 'y') { $ln = 1; next; }
  0         0  
  0         0  
604             }elsif( $res == 0 ) {
605 0         0 print $OUT "\n\n\n\n\nvi: nothing saved\n";
606             }
607             }else{
608 0         0 print $OUT "\n\n\n\n\nvi: nothing saved\n";
609             }
610 0         0 last;
611             }
612             }else{
613 0         0 print $OUT "vi: no such file '$arg'\n";
614             }
615 0         0 unlink( $tmpf );
616             }else{
617 0         0 print $OUT "vi: error: illegal or too long filename '$arg'\n";
618             }
619             }else{
620 0         0 print $OUT "vi: error: read only directory '/$vwd'\n";
621             }
622             }else{
623 0         0 print $OUT "vi: usage: $commands{vi}{doc}\n";
624             }
625 0         0 return 0;
626             }
627              
628             ########################################################################
629             # com_wref()
630             #
631             sub com_wref() {
632 0     0 0 0 my ($arg,$x) = @_;
633 0 0 0     0 if( defined $arg and !defined $x ) {
634 0         0 my @reffiles = get_who_refs_me( $vwd, $arg );
635 0 0       0 if( $#reffiles > -1 ) {
636 0         0 print $OUT join( "\n", @reffiles );
637 0         0 print $OUT "\n";
638             }else{
639 0         0 print $OUT "wrefs: no one references '$arg'\n";
640             }
641             }else{
642 0         0 print $OUT "wrefs: usage: $commands{wrefs}{doc}\n";
643             }
644 0         0 return 0;
645             }
646              
647             ########################################################################
648             # com_vgrep()
649             #
650             sub com_vgrep() {
651 0     0 0 0 my ($arg,$x) = @_;
652 0 0 0     0 if( defined $arg and !defined $x ) {
653 0         0 do_vgrep( $arg );
654             }else{
655 0         0 print $OUT "vgrep: usage: $commands{vgrep}{doc}\n";
656             }
657 0         0 return 0;
658             }
659              
660              
661              
662              
663              
664              
665             ########################################################################
666             # c o m p l e t i o n
667             ########################################################################
668              
669             # from p5-ReadLine example 'FileManager'
670              
671             # Attempt to complete on the contents of TEXT. START and END bound
672             # the region of rl_line_buffer that contains the word to complete.
673             # TEXT is the word to complete. We can use the entire contents of
674             # rl_line_buffer in case we want to do some simple parsing. Return
675             # the array of matches, or NULL if there aren't any.
676             sub dbshell_completion {
677 0     0 0 0 my ($text, $line, $start, $end) = @_;
678            
679 0         0 my @matches = ();
680              
681             # If this word is at the start of the line, then it is a command
682             # to complete. Otherwise it is the name of a file in the current
683             # directory.
684 0 0       0 if ($start == 0) {
    0          
685 0         0 @matches = $term->completion_matches($text, \&command_generator);
686             }elsif($line =~ /^cd\s.*/ ) {
687 0         0 @matches = $term->completion_matches($text, \&vdir_generator);
688             }else{
689 0         0 @matches = $term->completion_matches($text, \&vfile_generator);
690             }
691              
692 0         0 return @matches;
693             }
694              
695             # from p5-ReadLine example 'FileManager'
696             # Generator function for command completion. STATE lets us know
697             # whether to start from scratch; without any state (i.e. STATE == 0),
698             # then we start at the top of the list.
699              
700             ## Term::ReadLine::Gnu has list_completion_function similar with this
701             ## function. I defined new one to be compared with original C version.
702             {
703             my $list_index;
704             my @name;
705              
706             sub command_generator {
707 0     0 0 0 my ($text, $state) = @_;
708 0         0 $text =~ s/\./\\\./g;
709 0         0 $text =~ s/\*/\\\*/g;
710 0         0 $text =~ s/\[/\\\[/g;
711 0         0 $text =~ s/\]/\\\]/g;
712 0         0 $text =~ s/\$/\\\$/g;
713 0         0 $text =~ s/\^/\\\^/g;
714              
715             # If this is a new word to complete, initialize now. This
716             # includes saving the length of TEXT for efficiency, and
717             # initializing the index variable to 0.
718 0 0       0 unless ($state) {
719 0         0 $list_index = 0;
720 0         0 @name = keys(%commands);
721             }
722              
723             # Return the next name which partially matches from the
724             # command list.
725 0         0 while ($list_index <= $#name) {
726 0         0 $list_index++;
727 0 0       0 return $name[$list_index - 1]
728             if ($name[$list_index - 1] =~ /^$text/);
729             }
730             # If no names matched, then return NULL.
731 0         0 return undef;
732             }
733             }
734              
735             {
736             my $list_index;
737             my @name;
738              
739             sub vdir_generator {
740 0     0 0 0 my ($text, $state) = @_;
741 0         0 $text =~ tr/a-zA-Z0-9_\///cd;
742 0         0 $text =~ s/\./\\\./g;
743 0         0 $text =~ s/\*/\\\*/g;
744 0         0 $text =~ s/\[/\\\[/g;
745 0         0 $text =~ s/\]/\\\]/g;
746 0         0 $text =~ s/\$/\\\$/g;
747 0         0 $text =~ s/\^/\\\^/g;
748            
749             # If this is a new word to complete, initialize now. This
750             # includes saving the length of TEXT for efficiency, and
751             # initializing the index variable to 0.
752 0 0       0 unless ($state) {
753 0         0 $list_index = 0;
754 0         0 @name = keys(%{$vdirs});
  0         0  
755             }
756              
757             # Return the next name which partially matches
758 0         0 while ($list_index <= $#name) {
759 0         0 $list_index++;
760 0 0       0 return $name[$list_index - 1]
761             if ($name[$list_index - 1] =~ /^$text/);
762             }
763             # If no names matched, then return NULL.
764 0         0 return undef;
765             }
766             }
767              
768             {
769             my $list_index;
770             my @name;
771              
772             sub vfile_generator {
773 0     0 0 0 my ($text, $state) = @_;
774 0         0 $text =~ tr/a-zA-Z0-9_\///cd;
775 0         0 $text =~ s/\./\\\./g;
776 0         0 $text =~ s/\*/\\\*/g;
777 0         0 $text =~ s/\[/\\\[/g;
778 0         0 $text =~ s/\]/\\\]/g;
779 0         0 $text =~ s/\$/\\\$/g;
780 0         0 $text =~ s/\^/\\\^/g;
781              
782 0 0       0 unless ($state) {
783 0         0 undef @name;
784 0         0 $list_index = 0;
785 0         0 my $st;
786 0         0 my $col = $vdirs->{$vwd}{fnamcol};
787 0         0 $st = $dbh->prepare("select $col from $vwd order by $col");
788 0 0       0 unless( $st ) {
789 0         0 print $OUT "$PRG: prep completion query '$vwd':\n " . $dbh->errstr;
790 0         0 return undef;
791             }
792 0 0       0 unless( $st->execute() ) {
793 0         0 print $OUT "$PRG: exec completion query '$vwd':\n " . $dbh->errstr;
794 0         0 return undef;
795             }
796 0         0 my $i;
797 0         0 while( $i = $st->fetchrow_array() ) {
798 0         0 push @name, $i;
799             }
800 0         0 $st->finish();
801             }
802              
803             # Return the next name which partially matches
804 0         0 while ($list_index <= $#name) {
805 0         0 $list_index++;
806 0 0       0 return $name[$list_index - 1]
807             if ($name[$list_index - 1] =~ /^$text/);
808             }
809             # If no names matched, then return NULL.
810 0         0 return undef;
811             }
812             }
813              
814             ########################################################################
815             # c h e c k i n g & c r e a t i o n
816             ########################################################################
817              
818             ########################################################################
819             # check_vdirs_struct()
820             #
821             sub check_vdirs_struct() {
822 0     0 0 0 my $pre = "internal error: vdirs structure:\n ";
823 0         0 foreach my $dir (keys(%{$vdirs}) ) {
  0         0  
824             # init refby:
825             # a hash holding the dir (key) and list of columns (value) this dir
826             # is referenced by. Will be set up 57 lines later (# setup refby)
827 0         0 $vdirs->{$dir}->{refby} = {};
828             }
829              
830 0         0 foreach my $dir (sort keys(%{$vdirs}) ) {
  0         0  
831 0 0       0 $vwd = $dir unless defined $vwd; # set $vwd to alphabetic first dir
832              
833 0 0       0 unless( defined $vdirs->{$dir}->{desc}) {
834 0         0 print "$pre dir '$dir': 'desc' missing\n";
835 0         0 return 1;
836             }
837 0 0       0 unless( defined $vdirs->{$dir}->{edit}) {
838 0         0 print "$pre dir '$dir': 'edit' missing\n";
839 0         0 return 1;
840             }
841              
842 0 0       0 unless( defined $vdirs->{$dir}->{cols}) {
843 0         0 print "$pre dir '$dir': 'cols' missing\n";
844 0         0 return 1;
845             }
846              
847 0 0       0 unless( defined $vdirs->{$dir}->{refby}) {
848 0         0 print "$pre dir '$dir': 'refby' missing \n";
849 0         0 return 1;
850             }
851              
852 0         0 my $fnamcol = $vdirs->{$dir}{fnamcol};
853 0 0       0 unless( defined $fnamcol) {
854 0         0 print "$pre dir '$dir': 'fnamcol' missing\n";
855 0         0 return 1;
856             }
857 0 0       0 unless( defined $vdirs->{$dir}{cols}{$fnamcol} ) {
858 0         0 print "$pre dir '$dir', fnamcol set to '$fnamcol', but column missing\n";
859 0         0 return 1;
860             }
861 0 0       0 if( $vdirs->{$dir}{cols}{$fnamcol}{type} ne 'char' ) {
862 0         0 print "$pre dir '$dir', fnamcol-column '$fnamcol' type must be 'char'\n";
863 0         0 return 1;
864             }
865 0 0       0 if( $vdirs->{$dir}{edit} == 1 ) {
866 0 0       0 unless( defined $vdirs->{$dir}{cols}{$fnamcol}{len} ) {
867 0         0 print "$pre dir '$dir', fnamcol-column '$fnamcol': missing 'len'\n";
868 0         0 return 1;
869             }
870             }
871 0 0       0 if( $vdirs->{$dir}{cols}{$fnamcol}{len} + 2 > $LS_COL_WIDTH ) {
872 0         0 my $maxlen = $LS_COL_WIDTH - 2;
873 0         0 print "$pre dir '$dir', fnamcol-column '$fnamcol' len > $maxlen\n";
874 0         0 return 1;
875             }
876              
877 0         0 my $comcol = $vdirs->{$dir}{comcol};
878 0 0       0 if( defined $comcol) {
879 0 0       0 unless( defined $vdirs->{$dir}{cols}{$comcol} ) {
880 0         0 print "$pre dir '$dir', comcol set to '$comcol', but column missing\n";
881 0         0 return 1;
882             }
883 0 0       0 if( $vdirs->{$dir}{cols}{$comcol}{type} ne 'char' ) {
884 0         0 print "$pre dir '$dir', comcol-column '$comcol' type must be 'char'\n";
885 0         0 return 1;
886             }
887 0 0       0 unless( defined $vdirs->{$dir}{cols}{$comcol}{len} ) {
888 0         0 print "$pre dir '$dir', comcol-column '$comcol': missing 'len'\n";
889 0         0 return 1;
890             }
891             }
892              
893 0         0 my %varnames = (); # duplicate check: key=varname value=column name
894 0         0 my $cols = $vdirs->{$dir}{cols};
895 0         0 foreach my $col (keys(%{$cols} )) {
  0         0  
896             # check for deprecated 'delcp' option
897 0 0       0 if( exists $cols->{$col}{delcp} ) {
898 0         0 $cols->{$col}{uniq} = 1;
899 0         0 print "\nWARNING: $PRG: internal vdirs struct:\n dir '$dir', column '$col', option 'delcp' deprecated, use 'uniq'\n\n";
900             }
901              
902             # check for 'type' / 'ref'
903 0 0 0     0 unless( defined $cols->{$col}{type} || defined $cols->{$col}{ref} ) {
904 0         0 print "$pre dir '$dir', column '$col', either 'type' or 'ref' must be set\n";
905 0         0 return 1;
906             }
907 0 0 0     0 if( defined $cols->{$col}{ref} and !defined $vdirs->{$cols->{$col}{ref}}){
908 0         0 print "$pre dir '$dir', column '$col', elem 'ref': no dir '$cols->{$col}{ref}'\n";
909 0         0 return 1;
910             }
911              
912             # check for flags
913 0 0       0 if( exists $cols->{$col}{flags} ) {
914 0 0       0 if( $cols->{$col}{type} ne 'int' ) {
915 0         0 print "$pre dir '$dir', column '$col', when using 'flags' type must be 'int'\n";
916 0         0 return 1;
917             }
918 0 0       0 unless( ref( $cols->{$col}{flags} ) eq "HASH" ) {
919 0         0 print "$pre dir '$dir', column '$col', 'flags' must be a hash\n";
920 0         0 return -1;
921             }
922 0         0 foreach my $i (sort keys(%{$cols->{$col}{flags} }) ) {
  0         0  
923 0 0       0 if( $i =~ /\D/ ) {
924 0         0 print "$pre dir '$dir', column '$col', flags: bitno must be an int\n";
925 0         0 return 1;
926             }
927 0 0       0 unless( ref( $cols->{$col}{flags}{$i} ) eq "ARRAY" ) {
928 0         0 print "$pre dir '$dir', column '$col', bitno '$i': missing array with flagname + flagdescritpion\n";
929 0         0 return -1;
930             }
931 0 0       0 unless( defined $cols->{$col}{flags}{$i}->[0] ) {
932 0         0 print "$pre dir '$dir', column '$col', bitno '$i': missing flagname\n";
933 0         0 return -1;
934             }
935 0 0       0 if( $cols->{$col}{flags}{$i}->[0] =~ / / ) {
936 0         0 print "$pre dir '$dir', column '$col', bitno '$i': flagname must be a single word\n";
937 0         0 return -1;
938             }
939 0 0       0 unless( defined $cols->{$col}{flags}{$i}->[1] ) {
940 0         0 print "$pre dir '$dir', column '$col', bitno '$i': missing flagdescription\n";
941 0         0 return -1;
942             }
943             }
944             }
945              
946             # check for enums
947 0 0       0 if( exists $cols->{$col}{enums} ) {
948 0 0       0 if( $cols->{$col}{type} ne 'int' ) {
949 0         0 print "$pre dir '$dir', column '$col', when using 'enums' type must be 'int'\n";
950 0         0 return 1;
951             }
952 0 0       0 unless( ref( $cols->{$col}{enums} ) eq "HASH" ) {
953 0         0 print "$pre dir '$dir', column '$col', 'enums' must be a hash\n";
954 0         0 return -1;
955             }
956 0         0 foreach my $i (sort keys(%{$cols->{$col}{enums} }) ) {
  0         0  
957 0 0       0 if( $i =~ /\D/ ) {
958 0         0 print "$pre dir '$dir', column '$col', enums: enumvalue must be an int\n";
959 0         0 return 1;
960             }
961 0 0       0 unless( ref( $cols->{$col}{enums}{$i} ) eq "ARRAY" ) {
962 0         0 print "$pre dir '$dir', column '$col', enumvalue '$i': missing array with enumname + enumdescritpion\n";
963 0         0 return -1;
964             }
965 0 0       0 unless( defined $cols->{$col}{enums}{$i}->[0] ) {
966 0         0 print "$pre dir '$dir', column '$col', enumvalue '$i': missing enumname\n";
967 0         0 return -1;
968             }
969 0 0       0 if( $cols->{$col}{enums}{$i}->[0] =~ / / ) {
970 0         0 print "$pre dir '$dir', column '$col', enumvalue '$i': enumname must be a single word\n";
971 0         0 return -1;
972             }
973 0 0       0 unless( defined $cols->{$col}{enums}{$i}->[1] ) {
974 0         0 print "$pre dir '$dir', column '$col', enumvalue '$i': missing enumdescription\n";
975 0         0 return -1;
976             }
977             }
978             }
979              
980             # setup refby
981 0 0       0 if( defined $cols->{$col}{ref} ) {
982 0         0 push @{$vdirs->{$cols->{$col}{ref}}{refby}{$dir} }, $col;
  0         0  
983             }
984              
985 0 0 0     0 if( defined $cols->{$col}{type} and $vdirs->{$dir}{edit}==1) {
986 0 0 0     0 if( $cols->{$col}{type} ne 'char' and
      0        
      0        
      0        
987             $cols->{$col}{type} ne 'int' and
988             $cols->{$col}{type} ne 'smallint' and
989             $cols->{$col}{type} ne 'inet' and
990             $cols->{$col}{type} ne 'cidr' )
991             {
992 0         0 print "$pre dir '$dir', column '$col', type must be one of char/int/smallint/inet/cidr when edit=1\n";
993 0         0 return 1;
994             }
995             }
996              
997 0 0       0 unless( defined $cols->{$col}{var} ) {
998 0         0 print "$pre dir '$dir', column '$col', missing elem 'var'\n";
999 0         0 return 1;
1000             }
1001 0 0       0 unless( defined $cols->{$col}{desc} ) {
1002 0         0 print "$pre dir '$dir', column '$col', missing elem 'desc'\n";
1003 0         0 return 1;
1004             }
1005 0 0       0 unless( defined $cols->{$col}{pos} ) {
1006 0         0 print "$pre dir '$dir', column '$col', missing elem 'pos'\n";
1007 0         0 return 1;
1008             }
1009              
1010             # check for duplicate var
1011 0         0 my $varname = $cols->{$col}{var};
1012 0 0       0 if( defined $varnames{ $varname } ) {
1013 0         0 print "$pre dir '$dir', var '$varname' used for columns '$col' and '$varnames{$varname}'\n";
1014 0         0 return 1;
1015             }
1016 0         0 $varnames{ $varname } = $col;
1017              
1018             }
1019             }
1020 0         0 return 0;
1021             }
1022              
1023             ########################################################################
1024             # check_db_tables()
1025             #
1026             sub check_db_tables() {
1027 0     0 0 0 my $st;
1028              
1029             # check version no of db tables
1030 0         0 $st = $dbh->prepare("select value from tablestatus where tag='version' ");
1031 0 0       0 unless( $st ) {
1032 0         0 print "$PRG: can't prepare query 'version':\n " . $dbh->errstr;
1033 0         0 return 1;
1034             }
1035 0 0       0 unless( $st->execute() ) {
1036 0         0 print "$PRG: can't execute query 'version':\n " . $dbh->errstr;
1037 0         0 return 1;
1038             }
1039            
1040 0         0 my ($dbversion) = $st->fetchrow_array();
1041 0 0       0 unless( $dbversion ) {
1042 0         0 print "$PRG: can't query db: table version\n";
1043 0         0 return 1;
1044             }
1045            
1046 0         0 $st->finish();
1047 0 0       0 if( $VERSION ne $dbversion ) {
1048 0         0 print
1049             "$PRG: version mismatch: $PRG='$VERSION' dbtables='$dbversion'\n";
1050 0         0 return 1;
1051             }
1052              
1053             # check (existence) of other tables
1054 0         0 foreach my $i (sort keys(%{$vdirs}) ) {
  0         0  
1055 0         0 $st = $dbh->prepare("select * from $i limit 1");
1056 0 0       0 unless( $st ) {
1057 0         0 print "$PRG: can't prepare query '$i':\n " . $dbh->errstr;
1058 0         0 return 1;
1059             }
1060 0 0       0 unless( $st->execute() ) {
1061 0         0 print "$PRG: can't execute query '$i':\n " . $dbh->errstr;
1062 0         0 return 1;
1063             }
1064 0         0 my @dummy = $st->fetchrow_array();
1065 0         0 $st->finish();
1066             }
1067 0         0 return 0; # all Ok
1068             }
1069              
1070              
1071              
1072             ########################################################################
1073             # recreate_db_tables();
1074             #
1075             sub recreate_db_tables() {
1076 0     0 0 0 my $r;
1077 0         0 $dbh->do( "drop table tablestatus" );
1078 0         0 $r = $dbh->do(
1079             qq{ create table tablestatus ("tag" char(16),
1080             "value" char(16) PRIMARY KEY) } );
1081 0 0       0 unless( $r ) {
1082 0         0 print "$PRG: create table tablestatus:\n " . $dbh->errstr;
1083 0         0 return;
1084             }
1085 0         0 $r = $dbh->do(
1086             qq{ insert into tablestatus (tag, value) values ('version','$VERSION' )});
1087 0 0       0 unless( $r ) {
1088 0         0 print "$PRG: insert version into tablestatus:\n " . $dbh->errstr;
1089 0         0 return;
1090             }
1091              
1092             # recreate other tables
1093 0         0 foreach my $tab (sort keys(%{$vdirs}) ) {
  0         0  
1094 0         0 $dbh->do( "drop table $tab" );
1095 0         0 my $create = "create table $tab (";
1096 0         0 my $cols = $vdirs->{$tab}{cols};
1097 0         0 foreach my $col (keys(%{$cols}) ) {
  0         0  
1098 0 0       0 if( defined $cols->{$col}{ref} ) {
1099 0         0 my $rdir = $cols->{$col}{ref};
1100 0         0 my $rfnc = $vdirs->{$rdir}{fnamcol};
1101 0 0       0 if( defined $vdirs->{$rdir}{cols}{$rfnc}{len} ) {
1102 0         0 $create .= "$col $vdirs->{$rdir}{cols}{$rfnc}{type}($vdirs->{$rdir}{cols}{$rfnc}{len})";
1103             }else{
1104 0         0 $create .= "$col $vdirs->{$rdir}{cols}{$rfnc}{type}";
1105             }
1106             }else{
1107 0 0       0 if( defined $cols->{$col}{len} ) {
1108 0         0 $create .= "$col $cols->{$col}{type}($cols->{$col}{len})";
1109             }else{
1110 0         0 $create .= "$col $cols->{$col}{type}";
1111             }
1112 0 0       0 $create .= " $cols->{$col}{colopt}" if defined $cols->{$col}{colopt};
1113             }
1114 0         0 $create .= ",";
1115             }
1116 0         0 chop $create;
1117 0         0 $create .= ")";
1118 0         0 $r = $dbh->do( $create );
1119 0 0       0 unless( $r ) {
1120 0         0 print "$PRG: create table $tab:\n " . $dbh->errstr;
1121 0         0 return;
1122             }
1123 0 0       0 my $df = $vdirs->{$tab}{defaultfile} if exists $vdirs->{$tab}{defaultfile};
1124 0 0       0 if( $df ) {
1125 0         0 my $fnc = $vdirs->{$tab}{fnamcol};
1126 0 0 0     0 if( (length($df)<=$vdirs->{$tab}{cols}{$fnc}{len}) and !($df=~/\W+/)) {
1127 0         0 $r = $dbh->do( "insert into $tab ($fnc) values ('$df')");
1128 0 0 0     0 if( !defined $r or $r==0 ) {
1129 0         0 print "ERROR: couldn't create default entry '$df' in '/$tab':" .
1130             $dbh->errstr;
1131             }
1132             }else{
1133 0         0 print "ERROR: illegal or to long default filename '$df' in '/$tab'\n";
1134             }
1135             }
1136             }
1137 0         0 return;
1138              
1139             } # recreate_db_tables()
1140              
1141             ########################################################################
1142             # print_file( FH, fnam, verbose );
1143             # create new pseudo file for cat/vi from database
1144             # FH: file handle for output
1145             # fnam: the filename (key from db)
1146             # verbose: 0: exclude comments, print only if fnam exists
1147             # 1: include comments, print only if fnam exists
1148             # 2: include comments, always print: print values if fnam exists,
1149             # else print NULL values
1150             # return:
1151             # 0: Ok
1152             # 1: file does not exist, (but NULL valued file was printed if verbose=2)
1153             # 2: other error
1154             #
1155             sub print_file() {
1156 0     0 0 0 my $FH = shift;
1157 0         0 my $fnam = shift;
1158 0         0 my $verbose = shift;
1159              
1160 0         0 my @vars;
1161             my @dbvars;
1162 0         0 my $var;
1163 0         0 my $maxvarlen = 0;
1164 0         0 my @values;
1165             my @defaults;
1166 0         0 my @descs;
1167 0         0 my @isref;
1168 0         0 my @flags;
1169 0         0 my @enums;
1170 0         0 my $select = "select ";
1171 0         0 my $retval = 2;
1172              
1173             # prepare db query
1174 0         0 my $fnc = $vdirs->{$vwd}{fnamcol};
1175 0         0 my $cols = $vdirs->{$vwd}{cols};
1176 0         0 foreach my $col (sort {$cols->{$a}{pos} <=> $cols->{$b}{pos}}
  0         0  
1177 0         0 keys(%{$cols}) )
1178             {
1179 0 0       0 next if $col eq $fnc;
1180 0         0 $var = $cols->{$col}{var};
1181 0 0       0 if( length($var) > $maxvarlen ) {$maxvarlen = length($var); }
  0         0  
1182 0         0 push @vars, $var;
1183 0         0 push @dbvars,$col;
1184 0         0 push @descs, $cols->{$col}{desc};
1185 0 0       0 push @isref, exists $cols->{$col}{ref} ? $cols->{$col}{ref} : undef;
1186 0 0       0 push @flags, exists $cols->{$col}{flags} ? $cols->{$col}{flags} : undef;
1187 0 0       0 push @enums, exists $cols->{$col}{enums} ? $cols->{$col}{enums} : undef;
1188 0         0 $select .= "$col,";
1189             }
1190 0         0 chop $select;
1191 0         0 $select .= " from $vwd where $fnc=?";
1192            
1193             # query db
1194 0         0 my $st;
1195 0         0 $st = $dbh->prepare( $select );
1196 0 0       0 unless( $st ) {
1197 0         0 print $FH "$PRG: can't prep print query '$vwd':\n " . $dbh->errstr;
1198 0         0 return 2;
1199             }
1200 0 0       0 unless( $st->execute( $fnam ) ) {
1201 0         0 print $FH "$PRG: can't exec print query 1 '$vwd' :\n " . $dbh->errstr;
1202 0         0 return 2;
1203             }
1204 0         0 @values = $st->fetchrow_array();
1205 0         0 $st->finish();
1206              
1207 0 0 0     0 if( $vdirs->{$vwd}{defaultfile} and $vdirs->{$vwd}{defaultfile} ne $fnam ) {
1208 0 0       0 unless( $st->execute( $vdirs->{$vwd}{defaultfile} ) ) {
1209 0         0 print $FH "$PRG: can't exec print query 2 '$vwd':\n " . $dbh->errstr;
1210 0         0 return 2;
1211             }
1212 0         0 @defaults = $st->fetchrow_array();
1213             }
1214 0         0 $st->finish();
1215              
1216             # print it
1217 0         0 my $em = "*unset*";
1218              
1219 0 0       0 if( $verbose == 0 ) {
1220 0 0       0 if( @values ) {
1221             # print short version (command 'sum')
1222 0         0 $retval = 0;
1223 0         0 for( my $i=0; $i<= $#values; $i++ ) {
1224 0 0       0 print $FH &var_value_s( $maxvarlen, $vars[$i], $values[$i],
1225             $defaults[$i], $flags[$i], $enums[$i],
1226             @defaults ? 1 : 0
1227             );
1228             }
1229             }else{
1230 0         0 $retval = 1;
1231             }
1232             }else{
1233             # verbose == 1: (print long) 2: (print long, even if file does not exist)
1234 0         0 my $newfilemsg = "";
1235 0         0 my $print_it = 0;
1236 0 0       0 if( @values ) {
1237             # file exists
1238 0         0 $retval = 0;
1239 0         0 $print_it = 1;
1240             }else{
1241             # file does not exist
1242 0         0 $retval = 1;
1243 0 0       0 if( $verbose == 2 ) {
1244 0         0 $newfilemsg = "#\n# NEW FILE NEW FILE NEW FILE NEW FILE\n#\n";
1245 0         0 $print_it = 1;
1246 0         0 for( my $i=0; $i<= $#vars; $i++ ) {
1247 0         0 $values[$i] = undef;
1248             }
1249             }
1250             }
1251 0 0       0 if( $print_it == 1 ) {
1252             # command 'cat/vi': long version
1253 0         0 print $FH "$newfilemsg" ;
1254 0         0 print $FH "#\n# Settings for $vdirs->{$vwd}{cols}{$fnc}{desc} '$fnam'" ;
1255 0 0 0     0 if( $vdirs->{$vwd}{defaultfile} and
1256             $vdirs->{$vwd}{defaultfile} ne $fnam ) {
1257 0         0 print $FH " (defaults: '$vdirs->{$vwd}{defaultfile}')";
1258             }
1259 0         0 print $FH "\n#\n".
1260             "# - this is a comment, comments always start in the first column.\n".
1261             "# - all lines begin in the first column or are blank lines\n".
1262             "# - a unset variable will write NULL into the database column\n";
1263 0 0 0     0 if( $vdirs->{$vwd}{defaultfile} and
1264             $vdirs->{$vwd}{defaultfile} ne $fnam ) {
1265 0         0 print $FH "# - unset variables use the default values\n";
1266             }
1267 0         0 print $FH "#\n";
1268 0         0 for( my $i=0; $i<= $#values; $i++ ) {
1269             # variable with comment header
1270 0         0 printf $FH "\n# %-50s(%s)\n", $vars[$i], $dbvars[$i];
1271 0         0 foreach my $descline (split '\n', $descs[$i] ) {
1272 0         0 print $FH "# $descline\n";
1273             }
1274 0         0 print $FH "#\n";
1275 0 0       0 if( @defaults ) {
1276 0         0 my $def;
1277 0 0 0     0 if( defined $defaults[$i] and defined $flags[$i] ) {
    0 0        
    0          
1278 0         0 $def = build_flags( $defaults[$i], $flags[$i] );
1279             }elsif( defined $defaults[$i] and defined $enums[$i] ) {
1280 0         0 $def = build_enums( $defaults[$i], $enums[$i] );
1281             }elsif( defined $defaults[$i] ) {
1282 0         0 $def = $defaults[$i];
1283             }
1284 0         0 print $FH "# default: ";
1285 0 0       0 print $FH defined $def ? "$def\n#\n" : "$em\n#\n";
1286             }
1287 0         0 print $FH &var_value_v( $vars[$i],$values[$i],$isref[$i],
1288             $flags[$i],$enums[$i] );
1289             }
1290 0         0 print $FH "\n# end of file '$fnam'\n";
1291             }
1292             }
1293 0         0 return $retval;
1294              
1295             } # print_file()
1296              
1297             ########################################################################
1298             # var_value_v( var, value, ref, flags, enums )
1299             # return a var = value string for verbose print_file()
1300             # var: variable name (long version for cat/vi)
1301             # value: the value of var or undef
1302             # ref: the dir/table referenced by this var or undef
1303             # flags: anon hashref with flags setup from vdir or undef
1304             # enums: anon hashref with enums setup from vdir or undef
1305             # return:
1306             # the string to be printed
1307             #
1308             sub var_value_v() {
1309 0     0 0 0 my ($var, $value, $ref, $flags, $enums ) = @_;
1310 0         0 my $s = '';
1311 0 0       0 if( defined $ref ) {
    0          
    0          
1312             # query db
1313 0         0 my $rval;
1314             my $st;
1315 0         0 my $select =
1316             "select $vdirs->{$ref}{fnamcol} from $ref order by $vdirs->{$ref}{fnamcol}";
1317 0         0 $s .= "# This is a reference to a file in dir '$ref'.\n";
1318 0         0 $st = $dbh->prepare( $select );
1319 0 0       0 unless( $st ) {
1320 0         0 $s .= "$PRG: can't prep var query '$ref':\n " . $dbh->errstr;
1321 0         0 return $s;
1322             }
1323 0 0       0 unless( $st->execute( ) ) {
1324 0         0 $s .= "$PRG: can't exec var query '$ref' :\n " . $dbh->errstr;
1325 0         0 return $s;
1326             }
1327              
1328 0         0 my $selected = '';
1329 0         0 my $available = '';
1330 0 0       0 $selected = " $value" if defined $value;
1331 0         0 while( ($rval) = $st->fetchrow_array() ) {
1332 0 0 0     0 next if defined $value and $value eq $rval;
1333 0         0 $available .= " $rval\n";
1334             }
1335 0         0 $st->finish();
1336 0         0 $s .= "$var = {\n Selected:\n$selected\n Available:\n$available}\n";
1337             }elsif( defined $flags ) {
1338 0         0 my $i;
1339 0         0 my $maxlen = 0;
1340 0         0 for( $i=0; $i<32; $i++ ) {
1341 0 0       0 if( exists $flags->{$i} ) {
1342 0 0       0 if( length( $flags->{$i}[0] ) > $maxlen ) {
1343 0         0 $maxlen = length( $flags->{$i}[0] );
1344             }
1345             }
1346             }
1347 0         0 $s .= "# Flags:\n";
1348 0 0       0 my $hash = defined $value ? '' : '#';
1349 0         0 my $on = "$hash On:\n";
1350 0         0 my $off = "$hash Off:\n";
1351 0         0 for( $i=0; $i<32; $i++ ) {
1352 0 0       0 if( exists $flags->{$i} ) {
1353 0         0 my $first = 1;
1354 0         0 foreach my $dscline (split '\n', $flags->{$i}[1] ) {
1355 0 0       0 if( $first ) {
1356 0         0 $first = 0;
1357 0         0 $s .= sprintf( "# %${maxlen}s: %s\n",$flags->{$i}[0], $dscline );
1358             }else{
1359 0         0 $s .= sprintf( "# %${maxlen}s %s\n", ' ', $dscline );
1360             }
1361             }
1362 0 0 0     0 if( defined $value and ($value & (1<<$i) ) ) {
1363 0         0 $on .= "$hash $flags->{$i}[0]\n";
1364             }else{
1365 0         0 $off .= "$hash $flags->{$i}[0]\n";
1366             }
1367             }
1368             }
1369 0 0       0 if( defined $value ) {
1370 0         0 $s .= "#\n$var = {\n$on$off}\n";
1371             }else{
1372 0         0 $s .= "#\n$var = { # undefined!\n$on$off}\n";
1373             }
1374              
1375             }elsif( defined $enums ) {
1376 0         0 my $i;
1377 0         0 my $maxlen = 0;
1378 0         0 foreach $i (keys(%{$enums}) ) {
  0         0  
1379 0 0       0 if( length( $enums->{$i}[0] ) > $maxlen ) {
1380 0         0 $maxlen = length( $enums->{$i}[0] );
1381             }
1382             }
1383 0         0 $s .= "# Enums:\n";
1384 0         0 my $selected = " Selected:\n";
1385 0         0 my $avail = " Available:\n";
1386 0 0 0     0 if( defined $value and !exists $enums->{$value} ) {
1387 0         0 $selected .= " *unknown-enum-value*\n";
1388             }
1389 0         0 foreach $i (sort { $a <=> $b } keys(%{$enums}) ) {
  0         0  
  0         0  
1390 0         0 my $first = 1;
1391 0         0 foreach my $dscline (split '\n', $enums->{$i}[1] ) {
1392 0 0       0 if( $first ) {
1393 0         0 $first = 0;
1394 0         0 $s .= sprintf( "# %${maxlen}s: %s\n",$enums->{$i}[0], $dscline );
1395             }else{
1396 0         0 $s .= sprintf( "# %${maxlen}s %s\n", ' ', $dscline );
1397             }
1398             }
1399 0 0 0     0 if( defined $value and $value == $i) {
1400 0         0 $selected .= " $enums->{$i}[0]";
1401             }else{
1402 0         0 $avail .= " $enums->{$i}[0]\n";
1403             }
1404             }
1405 0         0 $s .= "#\n$var = {\n$selected\n$avail}\n";
1406              
1407             }else{
1408 0         0 $s .= "$var = ";
1409 0 0       0 $s .= "$value" if defined $value;
1410 0         0 $s .= "\n";
1411             }
1412 0         0 return $s;
1413              
1414             } # var_value_v()
1415              
1416             ########################################################################
1417             # var_value_s( aligned, var, value, flags, enums, hasdefault )
1418             # return a var = value string for short output (sum & vgrep)
1419             # maxvarlen: if not 0: align all '=' using $maxvarlen, else: no alignment
1420             # var: variable name (long version for cat/vi)
1421             # value: the value of var or undef
1422             # default: the default value of var or undef
1423             # flags: anon hashref with flags setup from vdir or undef
1424             # enums: anon hashref with enums setup from vdir or undef
1425             # hasdefault: 1: we have a defaults file 0: we don't have
1426             # return:
1427             # the string to be printed
1428             #
1429             sub var_value_s() {
1430 0     0 0 0 my ($maxvarlen,$var,$value,$default,$flags,$enums,$hasdefault) = @_;
1431 0         0 my $s = '';
1432 0         0 my $i;
1433              
1434 0 0       0 if( defined $flags ) {
    0          
1435 0         0 $value = build_flags( $value, $flags );
1436 0         0 $default = build_flags( $default, $flags );
1437             }elsif( defined $enums ) {
1438 0         0 $value = build_enums( $value, $enums );
1439 0         0 $default = build_enums( $default, $enums );
1440             }
1441              
1442 0 0       0 if( $maxvarlen ) {
1443 0         0 $s = sprintf( "%-${maxvarlen}s ", $var );
1444             }else{
1445 0         0 $s = "$var ";
1446             }
1447 0 0       0 if( $hasdefault ) {
1448 0 0       0 if( defined $value ) {
1449 0         0 $s .= "= $value\n";
1450             }else{
1451 0 0       0 $s .= defined $default ? "-> $default\n" : "-> *unset*\n";
1452             }
1453             }else{
1454 0 0       0 $s .= defined $value ? "= $value\n" : "= *unset*\n";
1455             }
1456 0         0 return $s;
1457              
1458             } # var_value_s()
1459              
1460             ########################################################################
1461             # build_flags( value, flags )
1462             # return a string containing all flags set in value
1463             # value: the value of var or undef
1464             # flags: anon hashref with flags setup from vdir or undef
1465             # return:
1466             # the string of set flags if any flags are set
1467             # '' if no flags set
1468             # undef if value or flags is undef
1469             #
1470             sub build_flags() {
1471 0     0 0 0 my ( $value, $flags ) = @_;
1472 0         0 my $s;
1473             my $i;
1474              
1475 0 0 0     0 if( defined $flags and defined $value ) {
1476 0         0 $s = '';
1477 0         0 for( $i=0; $i<32; $i++ ) {
1478 0 0       0 if( exists $flags->{$i} ) {
1479 0 0       0 if( $value & (1<<$i) ) {
1480 0         0 $s .= "$flags->{$i}[0],";
1481             }
1482             }
1483             }
1484 0         0 chop $s; # chop ,
1485             }
1486 0         0 return $s;
1487              
1488             } # build_flags()
1489              
1490             ########################################################################
1491             # build_enums( value, enums )
1492             # return a string containing the enum set in value
1493             # value: the value of var or undef
1494             # enums: anon hashref with enums setup from vdir or undef
1495             # return:
1496             # the string of set enum if enum is set
1497             # undef if - value is undef
1498             # - enum is undef
1499             # - value is not contained in enums
1500             #
1501             sub build_enums() {
1502 0     0 0 0 my ( $value, $enums ) = @_;
1503 0         0 my $s;
1504             my $i;
1505              
1506 0 0 0     0 if( defined $enums and defined $value ) {
1507 0 0       0 if( exists $enums->{$value} ) {
1508 0         0 $s = "$enums->{$value}[0]";
1509             }else{
1510 0         0 $s = "*unknown-enum-value*";
1511             }
1512             }
1513 0         0 return $s;
1514              
1515             } # build_enums()
1516              
1517              
1518             ########################################################################
1519             # get_who_refs_me( dir, file )
1520             # return all files referenced by FILE
1521             # dir: an existing directory
1522             # file: the (probably existing) file within DIR which references
1523             # to be checked
1524             # return:
1525             # - a list of strings in format "dir/file" if references are found
1526             # - empty list if no references are found
1527             # - a list whith one entry holding the errormessage in case of an error
1528             #
1529             sub get_who_refs_me() {
1530 0     0 0 0 my ($dir,$file) = @_;
1531 0         0 my @res = ();
1532              
1533 0         0 foreach my $refdir (sort keys(%{$vdirs->{$dir}{refby}}) ) {
  0         0  
1534 0         0 my $select = "select $vdirs->{$refdir}{fnamcol} from $refdir where ";
1535 0         0 my @rcols = @{$vdirs->{$dir}{refby}{$refdir}};
  0         0  
1536 0         0 my $st;
1537 0         0 map { $_ .= "='$file'" } @rcols;
  0         0  
1538 0         0 $select .= join( " or ", @rcols );
1539 0         0 $select .= " order by $vdirs->{$refdir}{fnamcol}";
1540              
1541 0         0 $st = $dbh->prepare( $select );
1542 0 0       0 unless( $st ) {
1543 0         0 push @res,"$PRG: can't prep wrefs query '$file':\n " . $dbh->errstr;
1544 0         0 return @res;
1545             }
1546 0 0       0 unless( $st->execute( ) ) {
1547 0         0 push @res,"$PRG: can't exec wrefs query '$file':\n " . $dbh->errstr;
1548 0         0 return @res;
1549             }
1550 0         0 my $reffile;
1551 0         0 while( ($reffile) = $st->fetchrow_array() ) {
1552 0         0 push @res, "$refdir/$reffile";
1553             }
1554 0         0 $st->finish();
1555             }
1556 0         0 return @res;
1557             }
1558              
1559             ########################################################################
1560             # create_sql_from_file( tempfile, dir, vfile, insert_flag );
1561             #
1562             # tmpfile: Absolute path to temporary file on local disk holding
1563             # the edited parameters
1564             # vdir: exisiting virtual dir (table)
1565             # vfile: A file (db-row) for which to generate the $sql SQL code
1566             # insert_flag: 0: $sql --> 'update' string 1: $sql --> 'insert' string
1567             #
1568             # return
1569             # a list: ($lineno,$err, $sql):
1570             # - $lineno: when an error was detected: the errornous line
1571             # - $err: when an error was detected: a one line error text, else: undef
1572             # when $err is set then $sql is invalid
1573             # - $sql: when no error was detected: a SQL insert/update string or ''
1574             # if nothing to do, when $err is set: trash or undef
1575             #
1576             #
1577             sub create_sql_from_file( ) {
1578 0     0 0 0 my ($tmpfile,$vdir,$vfile,$insert_flag) = @_;
1579 0         0 my $lineno = 0;
1580 0         0 my $line;
1581             my $var;
1582 0         0 my $val;
1583 0         0 my $err;
1584 0         0 my $sql1;
1585 0         0 my $sql2;
1586 0         0 my %varcol; # translataion varname -> columnname
1587 0         0 my %isset; # flags: variable already set? 1: yes
1588 0         0 my %filevars; # variables from file for phase 2
1589 0         0 my %filevarslineno; # lineno of variables from file for phase 2
1590              
1591 0 0       0 if( $insert_flag ) {
1592 0         0 $sql1 = "insert into $vdir ($vdirs->{$vdir}{fnamcol},";
1593 0         0 $sql2 = " values('$vfile',";
1594             }else{
1595 0         0 $sql1 = "update $vdir set ";
1596 0         0 $sql2 = " where $vdirs->{$vdir}{fnamcol}='$vfile'";
1597             }
1598             # setup varname translation
1599 0         0 my $cols = $vdirs->{$vdir}{cols};
1600 0         0 foreach my $col ( keys( %{$cols} ) ) {
  0         0  
1601 0         0 $varcol{ $cols->{$col}{var} } = $col;
1602             }
1603              
1604             # phase 1: do the basic checks, remember var values and their lineno for
1605             # phase 2 check (user supplied check functions)
1606 0 0       0 open( TF, $tmpfile ) or return ( 1,"can't open tempfile '$tmpfile'", undef );
1607 0         0 MAIN: while( ) {
1608 0         0 $line = $_;
1609 0         0 $lineno++;
1610 0         0 chop( $line );
1611 0         0 $line =~ s/^\s*//; # remove leading space
1612 0 0       0 next MAIN if $line =~ /^$/; # skip empty lines
1613 0 0       0 next MAIN if $line =~ /^\#.*/;# skip comment lines
1614 0 0       0 unless( $line =~ /=/ ) { # missing = ?
1615 0         0 $err = "line $lineno: missing '='";
1616 0         0 last MAIN;
1617             }
1618 0         0 ($var,$val) = split( /=/, $line, 2 );
1619 0         0 $var =~ s/\s*$//; # remove trailing space
1620 0         0 $val =~ s/^\s*//; # remove leading space
1621 0         0 $val =~ s/\s*$//; # remove trailing space
1622              
1623 0 0 0     0 if( length($var)==0 or $var =~ /\W+/ ) { # var name ok?
1624 0         0 $err = "line $lineno: syntax error";
1625 0         0 last MAIN;
1626             }
1627              
1628             # check if variable name exists
1629 0 0       0 if( defined $varcol{$var} ) {
1630 0 0       0 if( defined $isset{$var} ) {
1631 0         0 $err = "line $lineno: variable '$var' set twice";
1632 0         0 last MAIN;
1633             }
1634              
1635 0         0 my $col = $varcol{$var};
1636 0         0 my $vlen = length( $val );
1637 0 0       0 if( $vlen > 0 ) {
1638             # check types
1639 0 0       0 if( defined $cols->{$col}{ref} ) {
    0          
    0          
    0          
    0          
    0          
1640             # type ref
1641              
1642             # 1st parse ref section like an enum to get the 'real' $val
1643 0 0       0 if( $val eq '{' ) {
1644 0         0 undef $val;
1645 0         0 my $mode = '{';
1646 0         0 my $l;
1647 0         0 VALREF: while( defined ( $l = ) ) {
1648 0         0 chop( $l );
1649 0         0 $lineno++;
1650 0         0 $l =~ s/\s*$//; # remove trailing space
1651 0         0 $l =~ s/^\s*//; # remove leading space
1652 0 0       0 next VALREF if $l =~ /^$/; # skip empty lines
1653 0 0       0 next VALREF if $l =~ /^\#.*/; # skip comment lines
1654 0 0       0 if( $l eq 'Selected:' ) { $mode = 'sel'; next VALREF; }
  0         0  
  0         0  
1655 0 0       0 if( $l eq 'Available:' ) { $mode = 'ava'; next VALREF; }
  0         0  
  0         0  
1656 0 0       0 if( $l eq '}' ) { $mode = '}'; last VALREF; }
  0         0  
  0         0  
1657 0 0       0 if( $mode eq 'sel' ) {
1658 0 0       0 if( ! defined $val ) {
1659 0         0 $val = $l;
1660             }else{
1661 0         0 $err = "line $lineno: only one elem may be selected for '$var'";
1662 0         0 last MAIN;
1663             }
1664             }
1665             } # loop VALREF
1666 0 0       0 if( $mode ne '}' ) {
1667 0         0 $err = "line $lineno: missing '}' from refs section";
1668 0         0 last MAIN;
1669             }
1670             }else{
1671 0         0 $err = "line $lineno: refs must start with '{'";
1672 0         0 last MAIN;
1673             }
1674              
1675             # now we have a $val (probably undef!), process it
1676 0         0 my $rdir = $cols->{$col}{ref};
1677 0         0 my $rfnc = $vdirs->{$rdir}{fnamcol};
1678 0 0       0 if( defined $vdirs->{$rdir}{cols}{$rfnc}{len} ) {
1679 0         0 my $rlen = $vdirs->{$rdir}{cols}{$rfnc}{len};
1680 0 0       0 if( $vlen > $rlen ) {
1681 0         0 $err = "line $lineno: value longer than $rlen";
1682 0         0 last MAIN;
1683             }
1684             }else{
1685 0 0       0 if( $vlen > 1 ) {
1686 0         0 $err = "line $lineno: value longer than 1";
1687 0         0 last MAIN;
1688             }
1689             }
1690             # check if val exists in referenced table
1691 0 0       0 if( defined $val ) {
1692 0         0 my $st;
1693             my $dbval;
1694 0         0 $st = $dbh->prepare("select $rfnc from $rdir where $rfnc=?");
1695 0 0       0 unless( $st ) {
1696 0         0 $err = "$PRG: internal error: prepare 'exist' query '$rdir':\n ";
1697 0         0 $err .= $dbh->errstr;
1698 0         0 last MAIN;
1699             }
1700 0 0       0 unless( $st->execute( $val ) ) {
1701 0         0 $err = "$PRG: internal error: exec 'exist' query '$rdir':\n ";
1702 0         0 $err .= $dbh->errstr;
1703 0         0 last MAIN;
1704             }
1705 0         0 $dbval = $st->fetchrow_array();
1706 0         0 $st->finish();
1707 0 0       0 unless( defined $dbval ) {
1708 0         0 $err = "line $lineno: reference '$val' does no exist in '$rdir'";
1709 0         0 last MAIN;
1710             }
1711             }
1712              
1713 0 0       0 my $sqlval = defined $val ? $dbh->quote( $val ) : "NULL";
1714 0 0       0 if( $insert_flag ) {
1715 0         0 $sql1 .= "$col,";
1716 0         0 $sql2 .= "$sqlval,";
1717             }else{
1718 0         0 $sql1 .= "$col=$sqlval,";
1719             }
1720 0         0 $filevars{$col} = $val;
1721 0         0 $filevarslineno{$col} = $lineno;
1722              
1723             }elsif( $cols->{$col}{type} eq 'char' ) {
1724             # type char
1725 0 0       0 if( defined $cols->{$col}{len} ) {
1726 0 0       0 if( $vlen > $cols->{$col}{len} ) {
1727 0         0 $err = "line $lineno: value longer than $cols->{$col}{len}";
1728 0         0 last MAIN;
1729             }
1730             }else{
1731 0 0       0 if( $vlen > 1 ) {
1732 0         0 $err = "line $lineno: value longer than 1";
1733 0         0 last MAIN;
1734             }
1735             }
1736 0 0       0 if( $insert_flag ) {
1737 0         0 $sql1 .= "$col,";
1738 0         0 $sql2 .= $dbh->quote( $val ) . ",";
1739             }else{
1740 0         0 $sql1 .= "$col=" . $dbh->quote( $val ) . ",";
1741             }
1742 0         0 $filevars{$col} = $val;
1743 0         0 $filevarslineno{$col} = $lineno;
1744              
1745             }elsif( $cols->{$col}{type} eq 'int' ) {
1746             # type int
1747 0 0       0 if( exists $cols->{$col}{flags} ) { # flags: process the flags
    0          
1748 0 0       0 if( $val =~ /{.*/ ) {
1749 0         0 $val = 0;
1750 0         0 my $mode = '{';
1751 0         0 my $l;
1752             my $flagfound;
1753 0         0 FLAGS: while( defined ( $l = ) ) {
1754 0         0 chop( $l );
1755 0         0 $lineno++;
1756 0         0 $l =~ s/\s*$//; # remove trailing space
1757 0         0 $l =~ s/^\s*//; # remove leading space
1758 0 0       0 next FLAGS if $l =~ /^$/; # skip empty lines
1759 0 0       0 next FLAGS if $l =~ /^\#.*/; # skip comment lines
1760 0 0       0 if( $l eq 'On:' ) { $mode = 'on'; next FLAGS; }
  0         0  
  0         0  
1761 0 0       0 if( $l eq 'Off:' ) { $mode = 'off'; next FLAGS; }
  0         0  
  0         0  
1762 0 0       0 if( $l eq '}' ) {
1763 0 0       0 $val = 'NULL' if $mode eq '{';
1764 0         0 $mode = '}';
1765 0         0 last FLAGS;
1766             }
1767 0         0 $flagfound = 0;
1768 0         0 foreach my $bit ( keys( %{$cols->{$col}{flags}} ) ) {
  0         0  
1769 0 0       0 if( $cols->{$col}{flags}{$bit}[0] eq $l ) {
1770 0         0 $flagfound = 1;
1771 0 0       0 $val |= (1<<$bit) if $mode eq 'on';
1772 0         0 last;
1773             }
1774             }
1775 0 0       0 unless( $flagfound ) {
1776 0         0 $err = "line $lineno: unknown flag '$l' for '$var'";
1777 0         0 last MAIN;
1778             }
1779             } # loop FLAGS
1780              
1781 0 0       0 if( $mode ne '}' ) {
1782 0         0 $err = "line $lineno: missing '}' from flags section";
1783 0         0 last MAIN;
1784             }
1785             }else{
1786 0         0 $err = "line $lineno: flags must start with '{'";
1787 0         0 last MAIN;
1788             }
1789             }elsif( exists $cols->{$col}{enums} ) { # enums: process the enums
1790              
1791 0 0       0 if( $val eq '{' ) {
1792 0         0 $val = 'NULL';
1793 0         0 my $mode = '{';
1794 0         0 my $l;
1795             my $enumfound;
1796 0         0 ENUMS: while( defined ( $l = ) ) {
1797 0         0 chop( $l );
1798 0         0 $lineno++;
1799 0         0 $l =~ s/\s*$//; # remove trailing space
1800 0         0 $l =~ s/^\s*//; # remove leading space
1801 0 0       0 next ENUMS if $l =~ /^$/; # skip empty lines
1802 0 0       0 next ENUMS if $l =~ /^\#.*/; # skip comment lines
1803 0 0       0 if( $l eq 'Selected:' ) { $mode = 'sel'; next ENUMS; }
  0         0  
  0         0  
1804 0 0       0 if( $l eq 'Available:' ) { $mode = 'ava'; next ENUMS; }
  0         0  
  0         0  
1805 0 0       0 if( $l eq '}' ) { $mode = '}'; last ENUMS; }
  0         0  
  0         0  
1806 0         0 $enumfound = 0;
1807 0         0 foreach my $enumval ( keys( %{$cols->{$col}{enums}} ) ) {
  0         0  
1808 0 0       0 if( $cols->{$col}{enums}{$enumval}[0] eq $l ) {
1809 0         0 $enumfound = 1;
1810 0 0       0 if( $mode eq 'sel' ) {
1811 0 0       0 if( $val eq 'NULL' ) {
1812 0         0 $val = $enumval;
1813             }else{
1814 0         0 $err = "line $lineno: only one elem may be selected for '$var'";
1815 0         0 last MAIN;
1816             }
1817             }
1818 0         0 last;
1819             }
1820             }
1821 0 0       0 unless( $enumfound ) {
1822 0         0 $err = "line $lineno: unknown enum tag '$l' for '$var'";
1823 0         0 last MAIN;
1824             }
1825             } # loop ENUMS
1826 0 0       0 if( $mode ne '}' ) {
1827 0         0 $err = "line $lineno: missing '}' from enums section";
1828 0         0 last MAIN;
1829             }
1830             }else{
1831 0         0 $err = "line $lineno: enums must start with '{'";
1832 0         0 last MAIN;
1833             }
1834             }else{ # no flags,no enums, normal int
1835 0 0       0 unless( $val =~ /^-?\d+$/ ) {
1836 0         0 $err = "line $lineno: value not an integer";
1837 0         0 last MAIN;
1838             }
1839 0 0 0     0 if( $val <= -2147483648 or $val >= 2147483647 ) {
1840 0         0 $err = "line $lineno: value out of int range";
1841 0         0 last MAIN;
1842             }
1843             }
1844 0 0       0 if( $insert_flag ) {
1845 0         0 $sql1 .= "$col,";
1846 0         0 $sql2 .= "$val,";
1847             }else{
1848 0         0 $sql1 .= "$col=$val,";
1849             }
1850              
1851             # workaroud: if enum and no elem was selected $val is 'NULL'
1852             # normally $val here is defined and has usefull value, the
1853             # empty/NULL case will be handled above ($vlen > 0) but enum
1854             # is special. $val must stay NULL to make the sql's
1855             # work, but the user supplied valok functions expect 'undef'
1856 0 0       0 if( $val eq 'NULL' ) {
1857 0         0 $filevars{$col} = undef;
1858             }else{
1859 0         0 $filevars{$col} = $val;
1860             }
1861 0         0 $filevarslineno{$col} = $lineno;
1862              
1863             }elsif( $cols->{$col}{type} eq 'smallint' ) {
1864             # type smallint
1865 0 0       0 unless( $val =~ /^-?\d+$/ ) {
1866 0         0 $err = "line $lineno: value not an integer";
1867 0         0 last MAIN;
1868             }
1869 0 0 0     0 if( $val <= -32768 or $val >= 32767 ) {
1870 0         0 $err = "line $lineno: value out of smallint range";
1871 0         0 last MAIN;
1872             }
1873 0 0       0 if( $insert_flag ) {
1874 0         0 $sql1 .= "$col,";
1875 0         0 $sql2 .= "$val,";
1876             }else{
1877 0         0 $sql1 .= "$col=$val,";
1878             }
1879 0         0 $filevars{$col} = $val;
1880 0         0 $filevarslineno{$col} = $lineno;
1881              
1882             }elsif( $cols->{$col}{type} eq 'cidr' ) {
1883             # type cidr
1884 0         0 my $st;
1885             my $dbval;
1886 0         0 $st = $dbh->prepare( "select cidr '$val'" );
1887 0 0       0 unless( $st ) {
1888 0         0 $err = "$PRG: internal error: select cidr\n ";
1889 0         0 $err .= $dbh->errstr;
1890 0         0 last MAIN;
1891             }
1892 0 0       0 unless( $st->execute( ) ) {
1893 0         0 $err = $dbh->errstr;
1894 0         0 last MAIN;
1895             }
1896 0         0 ($dbval) = $st->fetchrow_array();
1897 0         0 $st->finish();
1898              
1899 0 0       0 if( $insert_flag ) {
1900 0         0 $sql1 .= "$col,";
1901 0         0 $sql2 .= "'$val',";
1902             }else{
1903 0         0 $sql1 .= "$col='$val',";
1904             }
1905 0         0 $filevars{$col} = $val;
1906 0         0 $filevarslineno{$col} = $lineno;
1907              
1908             }elsif( $cols->{$col}{type} eq 'inet' ) {
1909             # type inet
1910 0         0 my $st;
1911             my $dbval;
1912 0         0 $st = $dbh->prepare( "select inet '$val'" );
1913 0 0       0 unless( $st ) {
1914 0         0 $err = "$PRG: internal error: select inet\n ";
1915 0         0 $err .= $dbh->errstr;
1916 0         0 last MAIN;
1917             }
1918 0 0       0 unless( $st->execute( ) ) {
1919 0         0 $err = $dbh->errstr;
1920 0         0 last MAIN;
1921             }
1922 0         0 ($dbval) = $st->fetchrow_array();
1923 0         0 $st->finish();
1924              
1925 0 0       0 if( $insert_flag ) {
1926 0         0 $sql1 .= "$col,";
1927 0         0 $sql2 .= "'$val',";
1928             }else{
1929 0         0 $sql1 .= "$col='$val',";
1930             }
1931 0         0 $filevars{$col} = $val;
1932 0         0 $filevarslineno{$col} = $lineno;
1933              
1934             }else{
1935             # type unknown!
1936 0         0 $err = "line $lineno: unsupported datatype from vdirs for $var";
1937 0         0 last MAIN;
1938             }
1939             }else{ # $vlen == 0
1940 0 0       0 if( $insert_flag ) {
1941 0         0 $sql1 .= "$col,";
1942 0         0 $sql2 .= "NULL,";
1943             }else{
1944 0         0 $sql1 .= "$col=NULL,";
1945             }
1946 0         0 $filevars{$col} = undef;
1947 0         0 $filevarslineno{$col} = $lineno;
1948             }
1949 0         0 $isset{$var} = 1; # remember that this var is set
1950             }else{
1951 0         0 $err = "line $lineno: unknown variable '$var'";
1952 0         0 last MAIN;
1953             }
1954             }
1955 0         0 close( TF );
1956 0 0       0 if( $insert_flag ) {
1957 0         0 chop( $sql1 );
1958 0         0 chop( $sql2 );
1959 0         0 $sql1 .= ")";
1960 0         0 $sql2 .= ")";
1961             }else{
1962 0 0       0 if( chop( $sql1 ) ne ',' ) {
1963             # no columns to update
1964 0         0 $sql1 = "";
1965 0         0 $sql2 = "";
1966             }
1967             }
1968              
1969             # phase 2: if basic check didn't show an error, do the user supplied checks
1970              
1971 0         0 my $hasuniqcols = 0;
1972 0         0 $filevars{ $vdirs->{$vdir}{fnamcol} } = $vfile; # add our filename to hash
1973 0 0       0 if( !defined $err ) {
1974 0         0 foreach my $col (keys(%filevars) ) {
1975 0         0 my $valerr;
1976 0 0       0 if( exists $cols->{$col}{uniq} ) { $hasuniqcols = 1; }
  0         0  
1977 0 0       0 if( exists $cols->{$col}{valok} ) {
1978 0         0 $valerr = &{$cols->{$col}{valok}}( $filevars{$col}, \%filevars, $dbh );
  0         0  
1979 0 0       0 if( defined $valerr ) {
1980 0         0 $err = "line $filevarslineno{$col}: $valerr";
1981 0         0 $lineno = $filevarslineno{$col};
1982 0         0 last;
1983             }
1984             }
1985             }
1986             }
1987              
1988             # phase 3: check if there are columns/vars that have to be uniq
1989              
1990 0         0 my $fnc = $vdirs->{$vwd}{fnamcol};
1991 0 0 0     0 if( !defined $err and $hasuniqcols == 1 ) {
1992 0         0 foreach my $col (keys(%filevars) ) {
1993 0         0 my $valerr = "";
1994 0 0 0     0 if( exists $cols->{$col}{uniq} and defined $filevars{$col} ) {
1995 0         0 my $st;
1996             my $dbval;
1997 0         0 $st = $dbh->prepare(
1998             "select $fnc from $vwd where $col=? and $fnc != '$vfile'");
1999 0 0       0 unless( $st ) {
2000 0         0 $err = "$PRG: internal error: prepare 'uniq' query '$vwd':\n ";
2001 0         0 $err .= $dbh->errstr;
2002 0         0 last;
2003             }
2004 0 0       0 unless( $st->execute( "$filevars{$col}" ) ) {
2005 0         0 $err = "$PRG: internal error: exec 'uniq' query '$vwd':\n ";
2006 0         0 $err .= $dbh->errstr;
2007 0         0 last;
2008             }
2009 0         0 while( ($dbval) = $st->fetchrow_array() ) {
2010 0         0 $valerr .= " $dbval";
2011             }
2012 0         0 $st->finish();
2013 0 0       0 if( $valerr ne "" ) {
2014 0         0 $err = "line $filevarslineno{$col}: uniq value '$filevars{$col}' " .
2015             "already in file(s): $valerr";
2016 0         0 $lineno = $filevarslineno{$col};
2017 0         0 last;
2018             }
2019             }
2020             }
2021             }
2022 0         0 return ( $lineno, $err, "$sql1$sql2" );
2023              
2024             } # create_sql_from_file()
2025              
2026             ########################################################################
2027             # want_to_edit_again( errortext )
2028             # ask the user if he wants to edit again
2029             # errortext: one line error text
2030             # return:
2031             # 'y' or 'n'
2032             #
2033             sub want_to_edit_again() {
2034 0     0 0 0 my $errortext = shift;
2035 0         0 my $inp = '';
2036 0         0 my $IN = $term->IN;
2037 0         0 print $OUT "\n\n\n\n\n\n\n$errortext\n";
2038 0   0     0 while( $inp ne 'y' and $inp ne 'n' ) {
2039 0         0 print $OUT "Do you want to edit again ('n' will abort) [y/n] ? ";
2040 0         0 $inp = <$IN>;
2041 0 0       0 $inp = '\n' unless defined $inp;
2042 0         0 chop $inp;
2043             }
2044 0         0 return $inp;
2045             }
2046              
2047             ########################################################################
2048             # do_vgrep( pattern );
2049             # grep all val/value pairs in vwd for pattern and print matching lines
2050             # pattern: the pattern to grep for
2051             #
2052             # return:
2053             # nothing
2054             #
2055             sub do_vgrep() {
2056 0     0 0 0 my $pattern = quotemeta shift;
2057              
2058 0         0 my @vars;
2059             my @dbvars;
2060 0         0 my $var;
2061 0         0 my @values;
2062 0         0 my @defaults;
2063 0         0 my @flags;
2064 0         0 my @enums;
2065 0         0 my $fnam;
2066 0         0 my $em = "*unset*";
2067 0         0 my $hasdefault;
2068              
2069             # prepare db query
2070 0         0 my $fnc = $vdirs->{$vwd}{fnamcol};
2071 0         0 my $select = "select $fnc,";
2072 0         0 my $seldef = "select ";
2073 0         0 my $cols = $vdirs->{$vwd}{cols};
2074 0         0 my $fnlen = $cols->{$fnc}{len};
2075 0         0 foreach my $col (sort {$cols->{$a}{pos} <=> $cols->{$b}{pos}}
  0         0  
2076 0         0 keys(%{$cols}) )
2077             {
2078 0 0       0 next if $col eq $fnc;
2079 0         0 $var = $cols->{$col}{var};
2080 0         0 push @vars, $var;
2081 0         0 push @dbvars,$col;
2082 0 0       0 push @flags, exists $cols->{$col}{flags} ? $cols->{$col}{flags} : undef;
2083 0 0       0 push @enums, exists $cols->{$col}{enums} ? $cols->{$col}{enums} : undef;
2084 0         0 $select .= "$col,";
2085 0         0 $seldef .= "$col,";
2086             }
2087 0         0 chop $select;
2088 0         0 chop $seldef;
2089 0         0 $select .= " from $vwd order by $fnc";
2090 0         0 $seldef .= " from $vwd where $fnc=?";
2091            
2092             # query default if available
2093 0 0       0 if( $vdirs->{$vwd}{defaultfile} ) {
2094 0         0 my $st;
2095 0         0 $st = $dbh->prepare( $seldef );
2096 0 0       0 unless( $st ) {
2097 0         0 print $OUT "$PRG: prep vgrep default query '$vwd':\n " . $dbh->errstr;
2098 0         0 return;
2099             }
2100 0 0       0 unless( $st->execute( $vdirs->{$vwd}{defaultfile} ) ) {
2101 0         0 print $OUT "$PRG: exec vgrep default query '$vwd':\n " . $dbh->errstr;
2102 0         0 return;
2103             }
2104 0         0 @defaults = $st->fetchrow_array();
2105 0         0 $st->finish();
2106             }
2107            
2108             # query all files
2109 0         0 my $st;
2110 0         0 $st = $dbh->prepare( $select );
2111 0 0       0 unless( $st ) {
2112 0         0 print $OUT "$PRG: prep vgrep query '$vwd':\n " . $dbh->errstr;
2113 0         0 return;
2114             }
2115 0 0       0 unless( $st->execute() ) {
2116 0         0 print $OUT "$PRG: exec vgrep query 1 '$vwd' :\n " . $dbh->errstr;
2117 0         0 return;
2118             }
2119              
2120             # print result
2121 0         0 while (($fnam, @values ) = $st->fetchrow_array() ) {
2122 0 0       0 if( @values ) {
2123 0         0 for( my $i=0; $i<= $#values; $i++ ) {
2124 0 0 0     0 if( $vdirs->{$vwd}{defaultfile} and
2125             $vdirs->{$vwd}{defaultfile} ne $fnam ) {
2126 0         0 $hasdefault = 1;
2127             }else{
2128 0         0 $hasdefault = 0;
2129             }
2130            
2131 0         0 my $line = &var_value_s( 0, $vars[$i], $values[$i],
2132             $defaults[$i], $flags[$i], $enums[$i],
2133             $hasdefault
2134             );
2135 0 0       0 printf $OUT "%${fnlen}s: %s", $fnam, $line if $line =~ /$pattern/i;
2136             }
2137             }
2138             }
2139 0         0 $st->finish();
2140            
2141 0         0 return;
2142             }
2143              
2144              
2145             ########################################################################
2146             ########################################################################
2147             ## obejct orientated interface: the access class for config database
2148             ########################################################################
2149             ########################################################################
2150              
2151             ########################################################################
2152             # new();
2153             # contructor for DBIx::FileSystem access class
2154             # parameter:
2155             # dbconn: database connect string used for DBI database connect
2156             # dbuser: database user
2157             # dbpasswd: database user's password
2158             # progdbver: program's databaase layout version string
2159             #
2160             # or
2161             # rdbh: reference to DBI database handle setup elsewhere
2162             # progdbver: program's databaase layout version string
2163             #
2164             # return: the object
2165             #
2166             sub new {
2167 2     2 0 207 my $class = shift;
2168 2         13 my %params = @_;
2169 2         6 my $self = {};
2170 2         4 bless( $self, $class );
2171              
2172 2         15 $self->{dbh} = undef;
2173 2         5 $self->{rdbh} = undef;
2174 2         7 $self->{err} = "Ok";
2175              
2176             # initialize object
2177 2 50 33     12 if( exists $params{rdbh} and defined( $params{rdbh} ) ){
2178 0 0 0     0 if( exists $params{progdbver} and defined( $params{progdbver} ) ) {
2179 0         0 $self->{progdbver} = $params{progdbver};
2180 0         0 $self->{rdbh} = $params{rdbh};
2181             }else{
2182 0         0 $self->{progdbver} = undef;
2183 0         0 $self->{err} = "parameter 'progdbver' undefined";
2184             }
2185             }else{
2186              
2187 2         4 FINI: while( 1 ) {
2188 2 50 33     17 if( exists $params{dbconn} and defined( $params{dbconn} ) ){
2189 2         6 $self->{dbconn} = $params{dbconn};
2190             }else{
2191 0         0 $self->{dbconn} = undef;
2192 0         0 $self->{err} = "parameter 'dbconn' undefined";
2193 0         0 last FINI;
2194             }
2195 2 50 33     11 if( exists $params{progdbver} and defined( $params{progdbver} ) ) {
2196 2         4 $self->{progdbver} = $params{progdbver};
2197             }else{
2198 0         0 $self->{progdbver} = undef;
2199 0         0 $self->{err} = "parameter 'progdbver' undefined";
2200 0         0 last FINI;
2201             }
2202             $self->{dbh} = DBI->connect( $self->{dbconn},
2203             $params{dbuser}, $params{dbpasswd},
2204 2         25 { PrintError => 0, AutoCommit => 1,
2205             ChopBlanks =>1 } );
2206 0 0       0 unless( $self->{dbh} ) {
2207 0         0 $self->{err} = "connect: " . $DBI::errstr;
2208 0         0 last FINI;
2209             }
2210 0         0 $self->{rdbh} = \$self->{dbh};
2211 0         0 last FINI;
2212             }
2213             }
2214              
2215 0         0 return $self;
2216              
2217             } # new()
2218              
2219             ########################################################################
2220             # DESTROY();
2221             # parameter: none
2222             #
2223             # return: nothing
2224             #
2225             sub DESTROY {
2226 2     2   2197 my $self = shift;
2227              
2228 2 50       12 $self->{dbh}->disconnect() if defined $self->{dbh};
2229              
2230 2         6 $self->{dbh} = undef;
2231 2         4 $self->{rdbh} = undef;
2232 2         4 $self->{dbconn} = undef;
2233 2         3 $self->{progdbver} = undef;
2234 2         5 $self->{err} = "object destroyed";
2235              
2236 2         11 return;
2237              
2238             } # DESTROY()
2239              
2240             ########################################################################
2241             # database_bad();
2242             # check if the database connection is ok. If not, set an errormessage into
2243             # the errorbuffer
2244             #
2245             # parameter: none
2246             #
2247             # return: 0: database ok
2248             # 1: database wrong
2249             #
2250             sub database_bad {
2251 0     0 0 0 my $self = shift;
2252 0         0 my $ret = 1;
2253              
2254 0 0       0 if( defined ${$self->{rdbh}} ) {
  0         0  
2255             # check version number
2256 0         0 my $st = ${$self->{rdbh}}->prepare(
  0         0  
2257             "SELECT value FROM tablestatus WHERE tag='version'" );
2258 0 0       0 if( $st ) {
2259 0 0       0 if( $st->execute() ) {
2260 0         0 my ($dbdbver) = $st->fetchrow_array();
2261 0 0       0 if( $dbdbver eq $self->{progdbver} ) {
2262 0         0 $ret = 0;
2263             }else{
2264             $self->{err} =
2265 0         0 "version mismatch: program <--> db ('$self->{progdbver}' <--> '$dbdbver')";
2266             }
2267 0         0 $st->finish();
2268             }else{
2269 0         0 $self->{err} = "exec query dbversion: " . ${$self->{rdbh}}->errstr;
  0         0  
2270             }
2271             }else{
2272 0         0 $self->{err} = "prepare qry dbversion: " . ${$self->{rdbh}}->errstr;
  0         0  
2273             }
2274             }
2275 0 0       0 if( $ret ) {
2276 0 0       0 $self->{dbh}->disconnect() if defined $self->{dbh};
2277 0         0 $self->{dbh} = undef;
2278             }
2279 0         0 return $ret;
2280              
2281             } # database_bad()
2282              
2283             ########################################################################
2284             # get_err();
2285             # read the last error message from the error buffer
2286             # parameter: none
2287             #
2288             # return: last errorstring
2289             #
2290             sub get_err {
2291 0     0 0 0 my $self = shift;
2292 0         0 return $self->{err};
2293              
2294             } # get_err()
2295              
2296             ########################################################################
2297             # get_conf_by_var();
2298             #
2299             # parameter:
2300             # in:
2301             # $dir: the directory (table) to search in
2302             # $defaultfname: the filename of the defaultfile if availalble,
2303             # else undef
2304             # $fnamcol: the column which contains the symbolic filename
2305             # in/out:
2306             # \%vars: a hashref pointing to a hash containing the column
2307             # names to fetch as a key, values will be set by function
2308             # in: \%searchvars a hashref pointing to a hash containing the values to
2309             # to use as a filter (SQL WHERE part). Key: column name
2310             # value: The value or an anon array-ref with
2311             # [ 'compare-operator', 'value-to-search-for' ]
2312             #
2313             # return: OK Ok, one file found, \%vars filled with values from db
2314             # NOFILE no file found, \%vars's values will be undef
2315             # NFOUND more than one file found, \%vars's values will be undef
2316             # ERROR error, call method get_err to pick the error message
2317             # \%vars's values will be undef
2318             #
2319             sub get_conf_by_var {
2320 0     0 0 0 my $self = shift;
2321 0         0 my $dir = shift;
2322 0         0 my $defaultfname =shift;
2323 0         0 my $fnamcol = shift;
2324 0         0 my $vars = shift;
2325 0         0 my $searchvars = shift;
2326              
2327 0         0 my $r = ERROR;
2328 0         0 my $st;
2329              
2330 0 0       0 unless( defined ${$self->{rdbh}} ) {
  0         0  
2331 0         0 $self->{err} = "DBIx::FileSystem database not initialized";
2332 0         0 return $r;
2333             }
2334              
2335             # check parameter
2336 0 0 0     0 if( !defined $dir or $dir eq '' ) {
2337 0         0 $self->{err} = "get_conf_by_var(): parameter 'dir' is empty";
2338 0         0 return $r;
2339             }
2340 0 0 0     0 if( !defined $fnamcol or $fnamcol eq '' ) {
2341 0         0 $self->{err} = "get_conf_by_var(): parameter 'fnamcol' is empty";
2342 0         0 return $r;
2343             }
2344 0 0       0 if( ref( $vars ) ne 'HASH' ) {
2345 0         0 $self->{err} = "get_conf_by_var(): parameter 'vars' is no hashref";
2346 0         0 return $r;
2347             }
2348 0 0       0 if( keys( %{$vars} ) == 0 ) {
  0         0  
2349 0         0 $self->{err} = "get_conf_by_var(): hash 'vars' is empty";
2350 0         0 return $r;
2351             }
2352 0 0       0 if( ref( $searchvars ) ne 'HASH' ) {
2353 0         0 $self->{err} = "get_conf_by_var(): parameter 'searchvars' is no hashref";
2354 0         0 return $r;
2355             }
2356 0 0       0 if( keys( %{$searchvars} ) == 0 ) {
  0         0  
2357 0         0 $self->{err} = "get_conf_by_var(): hash 'searchvars' is empty";
2358 0         0 return $r;
2359             }
2360              
2361 0         0 foreach my $v ( keys %{$vars} ) {
  0         0  
2362 0         0 $vars->{$v} = undef;
2363             }
2364              
2365 0         0 DB: while( 1 ) {
2366 0         0 my %extra;
2367 0         0 my $qry = '';
2368 0         0 my $res;
2369              
2370             # check query parameters against defaultfile
2371 0 0       0 if( defined $defaultfname ) {
2372 0         0 foreach my $searchvar (keys %{$searchvars} ) {
  0         0  
2373             $qry = "SELECT count($fnamcol) FROM $dir WHERE $searchvar" .
2374 0         0 $self->sqlize( $searchvars->{$searchvar} ) .
2375             " AND $fnamcol = '$defaultfname'";
2376 0         0 $st = ${$self->{rdbh}}->prepare( "$qry" );
  0         0  
2377 0 0       0 unless( $st ) {
2378 0         0 $self->{err} = "prepare extra qry: " . ${$self->{rdbh}}->errstr;
  0         0  
2379 0         0 last DB;
2380             }
2381 0 0       0 if( $st->execute() ) {
2382 0         0 ($res) = $st-> fetchrow_array();
2383 0 0 0     0 if( defined $res and $res == 1 ) {
2384 0         0 $extra{$searchvar} = " OR $searchvar IS NULL";
2385             }else{
2386 0         0 $extra{$searchvar} = "";
2387             }
2388 0         0 $st->finish();
2389             }else{
2390 0         0 $self->{err} = "exec extra qry: " . ${$self->{rdbh}}->errstr;
  0         0  
2391 0         0 last DB;
2392             }
2393             }
2394             }else{
2395 0         0 foreach my $searchvar (keys %{$searchvars} ) {
  0         0  
2396 0         0 $extra{$searchvar} = "";
2397             }
2398             }
2399              
2400             # base query
2401              
2402 0         0 $qry = "SELECT ";
2403 0         0 foreach my $var (keys %{$vars} ) {
  0         0  
2404 0         0 $qry .= "$var,";
2405             }
2406 0         0 chop $qry;
2407 0         0 $qry .= " FROM $dir WHERE ";
2408 0         0 my $rest = 0;
2409 0         0 foreach my $searchvar (keys %{$searchvars} ) {
  0         0  
2410 0 0       0 $qry .= " AND " if $rest;
2411 0         0 $qry .= "( $searchvar" . $self->sqlize( $searchvars->{$searchvar} ) .
2412             "$extra{$searchvar} )";
2413 0 0       0 $rest = 1 unless $rest;
2414             }
2415             ### print "qry: '$qry'\n";
2416              
2417 0         0 $res = ${$self->{rdbh}}->selectall_arrayref( $qry, { Slice => {} } );
  0         0  
2418 0 0 0     0 if( !defined $res or defined ${$self->{rdbh}}->{err} ) {
  0         0  
2419 0         0 $self->{err} = "get_conf_by_var(): query: " . ${$self->{rdbh}}->errstr;
  0         0  
2420 0         0 last DB;
2421             }
2422              
2423 0 0       0 if( @$res == 0 ) {
    0          
2424 0         0 $self->{err} = "no file found";
2425 0         0 $r = NOFILE;
2426 0         0 last DB;
2427             }elsif( @$res == 1 ) {
2428 0         0 foreach my $col (keys %{$res->[0]} ) {
  0         0  
2429 0         0 $vars->{$col} = $res->[0]->{$col};
2430             }
2431 0         0 $r = OK;
2432             }else{
2433 0         0 $self->{err} = "more than one file found";
2434 0         0 $r = NFOUND;;
2435 0         0 last DB;
2436             }
2437              
2438             # read defaults from defaultfile if necessary
2439              
2440 0 0       0 if( defined $defaultfname ) {
2441 0         0 my %sv = ( $fnamcol => $defaultfname );
2442 0         0 my %v = %{$vars};
  0         0  
2443 0         0 $r = $self->get_conf_by_var( $dir, undef, $fnamcol, \%v, \%sv );
2444              
2445 0 0       0 if( $r == OK ) {
2446 0         0 foreach my $var ( keys %v ) {
2447 0 0       0 $vars->{$var} = $v{$var} unless defined $vars->{$var};
2448             }
2449             }else{
2450 0 0       0 if( $r == NOFILE ) {
    0          
2451 0         0 $self->{err} = "defaultfile '$dir/$defaultfname' not found";
2452             }elsif( $r == NFOUND ) {
2453 0         0 $self->{err} = "more than one file '$dir/$defaultfname' found";
2454             }
2455 0         0 foreach my $v ( keys %{$vars} ) {
  0         0  
2456 0         0 $vars->{$v} = undef;
2457             }
2458 0         0 $r = ERROR;
2459             }
2460             }
2461              
2462 0         0 last DB;
2463              
2464             } # while( DB )
2465              
2466 0         0 return $r;
2467              
2468             } # get_conf_by_var()
2469              
2470             ########################################################################
2471             # sqlize();
2472             # build the right-hand-side of the WHERE part, incl. compare operator.
2473             # respect quoting of strings, integer values and 'undef' / NULL
2474             # parameter:
2475             # $val: the value to SQLize
2476             #
2477             # return: the sqlized string
2478             #
2479             sub sqlize {
2480 0     0 0 0 my $self = shift;
2481 0         0 my $val = shift;
2482 0         0 my $r;
2483 0 0       0 if( ! defined $val ) {
    0          
    0          
2484 0         0 $r = " IS NULL";
2485             }elsif( ref( $val ) eq 'ARRAY' ) {
2486 0 0 0     0 if( defined $val->[0] and defined $val->[1] ) {
2487 0 0       0 if( $self->isanumber( $val->[1] ) ) {
2488 0         0 $r = " $val->[0] $val->[1]";
2489             }else{
2490 0         0 $r = " $val->[0] " . ${$self->{rdbh}}->quote( $val->[1] );
  0         0  
2491             }
2492             }else{
2493 0         0 $r = " IS NULL";
2494             }
2495             }elsif( ref( $val ) eq '' ) { # scalar
2496 0 0       0 if( $self->isanumber( $val ) ) {
2497 0         0 $r = "=$val";
2498             }else{
2499 0         0 $r = "=" . ${$self->{rdbh}}->quote( $val );
  0         0  
2500             }
2501             }else{
2502 0         0 $r = "='-->INVALID-SEARCH-VALUE-SPECIFIED<--'";
2503             }
2504              
2505 0         0 return $r;
2506              
2507             } # sqlize()
2508              
2509              
2510             ########################################################################
2511             # isanumber();
2512             # check if $str is a number or not
2513             # parameter:
2514             # $str
2515             #
2516             # return: 0 $val is string
2517             # 1 $val is number
2518             #
2519             sub isanumber() {
2520 41     41 0 16464 my $self = shift;
2521 41         56 my $str = shift;
2522 41         46 my $r = 1;
2523              
2524 41 100       175 if( !defined $str ) {
    100          
    100          
    100          
    100          
2525 1         2 $r = 0;
2526             }elsif( $str eq '' ) {
2527 1         3 $r = 0;
2528             }elsif( $str =~ / / ) {
2529 7         11 $r = 0;
2530             }elsif( $str =~ /infinity/i ) {
2531 2         5 $r = 0;
2532             }elsif( $str =~ /nan/i ) {
2533 2         3 $r = 0;
2534             }else{
2535 28         44 $! = 0;
2536 28         102 my ($num, $unparsed ) = POSIX::strtod( $str );
2537 28 100 66     114 if( ($unparsed != 0) || $!) {
2538 9         12 $r = 0;
2539             }
2540             }
2541 41         100 return $r;
2542              
2543             } # isanumber()
2544              
2545              
2546             ########################################################################
2547             ########################################################################
2548             ########################################################################
2549             ########################################################################
2550              
2551             1;
2552             __END__