Added sudo to mnemosyne Console "interactivity" module
[mnemosyne.git] / mnemosyne.pl
blobf6e7a92037a3c02e6fb48e8a4e71f7e9fec0f835
1 #!/usr/bin/perl
2 # --- SDE-COPYRIGHT-NOTE-BEGIN ---
3 # This copyright note is auto-generated by ./scripts/Create-CopyPatch.
4 #
5 # Filename: target/mnemosyne/mnemosyne.pl
6 # Copyright (C) 2002 - 2006 Alejandro Mery
7 #
8 # More information can be found in the files COPYING and README.
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; version 2 of the License. A copy of the
13 # GNU General Public License can be found in the file COPYING.
14 # --- SDE-COPYRIGHT-NOTE-END ---
16 use warnings;
17 use strict;
18 use IPC::Open2;
20 use constant {ALL => 0, ASK => 1, CHOICE => 2 };
21 %::FOLDER=();
22 %::MODULE=();
24 sub scandir {
25 my ($pkgseldir,$prefix) = @_;
26 my %current=('location', $pkgseldir, 'var', "CFGTEMP_$prefix");
28 # $current{desc,var} for sub-pkgsel dirs
29 if ($pkgseldir ne $::ROOT) {
30 my ($relative,$dirvar,$dirname);
31 $_ = $pkgseldir;
32 $relative = (m/^$::ROOT\/(.*)/i)[0];
34 $dirvar = "CFGTEMP_$prefix\_$relative";
35 $dirvar =~ tr,a-z\/ ,A-Z__,;
37 $dirname=$relative;
38 $dirname=~ s/.*\///g;
40 ($current{desc} = $dirname) =~ s/_/ /g;
41 $current{var} = $dirvar;
44 # make this folder global
45 $::FOLDER{$current{var}} = \%current;
48 # make scandir recursive
49 my @children;
50 opendir(my $DIR, $pkgseldir);
51 foreach( grep { ! /^\./ } sort readdir($DIR) ) {
52 $_ = "$pkgseldir/$_";
53 if ( -d $_ ) {
54 my $subdir = scandir($_,$prefix);
55 push @children,$subdir;
56 } else {
57 my $module=scanmodule($_,$prefix,$current{var});
58 if ($module) {
59 push @children,$module unless grep(/^$module$/,@children);
63 closedir $DIR;
64 $current{children} = \@children;
65 return $current{var};
70 sub scanmodule {
71 my ($file,$prefix,$folder)=@_;
72 my (%current,$FILE);
74 # this defines dir,key,option and kind acording to the following format.
75 # $dir/[$prio-]$var[$option].$kind
76 do {
77 my ($dir,$key,$option,$kind);
78 m/^(.*)\/(\d+-)?([^\.]*).?([^\.]*)?\.([^\/\.]*)/i;
79 ($dir,$key,$option,$kind) = ($1,$3,$4,$5);
81 if ($kind eq 'choice') { $current{kind} = CHOICE; $current{option} = $option; }
82 elsif ($kind eq 'all') { $current{kind} = ALL; }
83 elsif ($kind eq 'ask') { $current{kind} = ASK; }
84 else { return; }
86 $current{location} = $dir;
87 $current{key} = $key;
88 $current{file} = $file;
90 } for $file;
92 open($FILE,'<',$file);
93 while(<$FILE>) {
94 if (/^#[^#: ]+: /) {
95 my ($field,$value) = m/^#([^#: ]+): (.*)$/i;
96 if ($field eq 'Description') {
97 $current{desc} = $value;
98 } elsif ($field eq 'Variable') {
99 $current{var} = $value;
100 } elsif ($field eq 'Default') {
101 $current{default} = $value;
102 } elsif ($field eq 'Forced') {
103 $current{forced} = $value;
104 } elsif ($field eq 'Imply') {
105 $current{imply} = $value;
106 } elsif ($field eq 'Dependencies') {
107 $current{deps} = $value;
108 # } else {
109 # print "$file:$field:$value.\n";
113 close($FILE);
115 # var name
116 $current{var} = uc $current{key}
117 unless exists $current{var};
118 $current{var} = "SDECFG_$prefix\_" . $current{var}
119 unless $current{var} =~ /^SDECFG_$prefix\_/;
121 # for choices, we use $option instead of $key as description
122 ($current{desc} = $current{option}) =~ s/_/ /g
123 if exists $current{option} && ! exists $current{desc};
124 ($current{desc} = $current{key}) =~ s/_/ /g
125 unless exists $current{desc};
127 # dependencies
128 # NOTE: don't use spaces on the pkgsel file, only to delimite different dependencies
129 if (exists $current{deps}) {
130 my @deps;
131 for ( split (/\s+/,$current{deps}) ) {
132 $_="SDECFG_$prefix\_$_" unless /^(SDECFG|CFGTEMP)/;
134 if (/=/) {
135 m/(.*?)(==|!=|=)(.*)/i;
136 $_="\"\$$1\" $2 $3";
137 } else {
138 $_="\"\$$_\" == 1";
141 push @deps,$_;
143 $current{deps} = \@deps;
146 # forced modules
147 if (exists $current{forced}) {
148 my @forced;
149 for ( split (/\s+/,$current{forced}) ) {
150 $_="SDECFG_$prefix\_$_" unless /^SDECFG/;
152 $_="$_=1" unless /=/;
153 push @forced,$_;
155 $current{forced} = \@forced;
158 # implied options
159 if (exists $current{imply}) {
160 my @imply = split (/\s+/,$current{imply});
161 $current{imply} = \@imply;
164 # make this module global
165 if ( $current{kind} == CHOICE ) {
167 # prepare the option for this choice
168 my %option;
169 for ('desc','forced','imply','deps','option','file') {
170 $option{$_}=$current{$_} if exists $current{$_};
173 if ( exists $::MODULE{$current{var}} ) {
174 push @{ $::MODULE{$current{var}}{options} },\%option;
175 } else {
176 # prepare and add this choice module
177 my @options = (\%option);
179 $::MODULE{$current{var}} = {
180 'kind', CHOICE,
181 'options', \@options,
184 for ('key','location','var') {
185 $::MODULE{$current{var}}{$_}=$current{$_}
186 if exists $current{$_};
190 } else {
191 $::MODULE{$current{var}} = {};
192 for ('key','location','var','desc','forced','deps','file','kind') {
193 $::MODULE{$current{var}}{$_}=$current{$_}
194 if exists $current{$_};
198 # default value
199 $::MODULE{$current{var}}{folder} = $folder;
200 $::MODULE{$current{var}}{default} = $current{default}
201 if exists $current{default};
203 return $current{var};
206 sub process_modules {
207 my ($READ,$WRITE,$pid);
208 my $i=0;
210 $pid = open2($READ, $WRITE, 'tsort');
211 # prepare topographic modules map
212 for my $module (values %::MODULE) {
213 my $related;
215 if ($module->{kind} == CHOICE) {
216 for (@{ $module->{options} }) {
217 my $option = $_;
218 for (@{exists $option->{deps} ? $option->{deps} : []} ) {
219 my $dep = (m/"\$([^"]+)"/i)[0];
220 print $WRITE "$dep $module->{var}\n";
221 $related=1;
223 for (@{exists $option->{forced} ? $option->{forced} : []} ) {
224 my $forced = (m/([^"]+)=/i)[0];
225 print $WRITE "$module->{var} $forced\n";
226 $related=1;
229 } else {
230 for (@{exists $module->{deps} ? $module->{deps} : []} ) {
231 my $dep = (m/"\$([^"]+)"/i)[0];
232 print $WRITE "$dep $module->{var}\n";
233 $related=1;
235 for (@{exists $module->{forced} ? $module->{forced} : []} ) {
236 my $forced = (m/([^"]+)=/i)[0];
237 print $WRITE "$module->{var} $forced\n";
238 $related=1;
242 if (! $related) {
243 print $WRITE "$module->{var} $module->{var}\n";
247 close($WRITE);
249 # and populate the sorted list
250 my @sorted;
251 while(<$READ>) {
252 if (/(.*)\n/) { push @sorted, $1; }
255 waitpid $pid,0;
256 die if $?;
258 # and remember the sorted list
259 $::MODULES=\@sorted;
262 sub process_folders {
263 my ($READ,$WRITE,$pid);
265 $pid = open2($READ, $WRITE, 'tsort | tac');
266 # prepare topographic modules map
267 for my $folder (values %::FOLDER) {
268 for ( exists $folder->{children} ? grep(!/^SDECFG/, @{$folder->{children}}) : [] ) {
269 print $WRITE "$folder->{var} $_\n";
272 close($WRITE);
274 # and populate the sorted list
275 my @sorted;
276 while(<$READ>) {
277 if (/(.*)\n/) { push @sorted, $1; }
280 waitpid $pid,0;
281 die if $?;
283 # and remember the sorted list
284 $::FOLDERS=\@sorted;
287 sub render_widgets_folder {
288 my ($folder,$offset) = @_;
289 for (@{$folder->{children}}) {
290 if (/^CFGTEMP/) {
291 my $subfolder=$::FOLDER{$_};
292 print "\n${offset}# $_\n${offset}#\n";
294 # opening
295 print "${offset}if [ \"\$$subfolder->{var}\" == 1 ]; then\n";
296 print "${offset}\tmenu_begin $subfolder->{var} '$subfolder->{desc}'\n";
297 print "${offset}fi\n";
299 render_widgets_folder($::FOLDER{$_},"$offset\t");
301 # closing
302 print "${offset}if [ \"\$$subfolder->{var}\" == 1 ]; then\n";
303 print "${offset}\tmenu_end\n";
304 print "${offset}fi\n";
305 } else {
306 my $module=$::MODULE{$_};
307 my $var=$module->{var};
308 my $conffile="$module->{location}/$module->{key}.conf"
309 if -f "$module->{location}/$module->{key}.conf";
310 my $noconffile="$module->{location}/$module->{key}-no.conf"
311 if -f "$module->{location}/$module->{key}-no.conf";
313 print "${offset}# $var\n";
315 if ($module->{kind} == CHOICE) {
316 # CHOICE
317 my $tmpvar = "CFGTEMP_$1" if $var =~ m/^SDECFG_(.*)/i;
318 my $listvar = "$tmpvar\_LIST";
319 my $defaultvar = "$tmpvar\_DEFAULT";
321 print "${offset}if \[ -n \"\$$var\" \]; then\n";
322 print "${offset}\tchoice $var \$$defaultvar \$$listvar\n";
323 print "${offset}\t. $conffile\n" if $conffile;
324 print "${offset}\telse\n" if $noconffile;
325 print "${offset}\t. $noconffile\n" if $noconffile;
326 print "${offset}fi\n";
328 } elsif ($module->{kind} == ASK) {
329 # ASK
330 my $default=0;
331 $default = $module->{default} if exists $module->{default};
333 print "${offset}if \[ -n \"\$$var\" \]; then\n";
334 print "${offset}\tbool '$module->{desc}' $module->{var} $default\n";
335 print "${offset}\t\[ \"\$$var\" == 1 \] && . $conffile\n" if $conffile;
336 print "${offset}\t\[ \"\$$var\" != 1 \] && . $noconffile\n" if $noconffile;
337 print "${offset}fi\n";
338 } elsif ($conffile) {
339 # ALL, only if $conffile
340 print "${offset}if \[ -n \"\$$var\" \]; then\n";
341 print "${offset}\t. $conffile\n" if $conffile;
342 print "${offset}\telse\n" if $noconffile;
343 print "${offset}\t. $noconffile\n" if $noconffile;
344 print "${offset}fi\n";
349 sub render_widgets {
350 open(my $FILE,'>',$_[0]);
351 my $root="CFGTEMP_$_[1]";
353 select $FILE;
354 render_widgets_folder($::FOLDER{$root},'');
355 select STDOUT;
356 close($FILE);
359 sub pkgsel_parse {
360 my ($action,$patternlist) = @_;
361 if ($action eq 'X' or $action eq 'x' ) {
362 $action = '$1="X"';
363 } elsif ($action eq 'O' or $action eq 'o') {
364 $action = '$1="O"';
365 } elsif ($action eq '-') {
366 $action = 'next';
367 } else {
368 $action = '{ exit; }';
371 my ($address,$first,$others)= ('','( ','&& ');
373 for (split(/\s+/,$patternlist)) {
374 if (! $address and $_ eq '!') {
375 $address = '! ';
376 $others = '|| $4"/"$5 ~';
377 } else {
378 $_="\*/$_" unless /\//;
379 s,[^a-zA-Z0-9_/\*+\.-],,g;
380 s,([/\.\+]),\\$1,g;
381 s,\*,[^/]*,g;
382 next unless $_;
383 $address = "$address$first";
384 $address = "$address / $_ /";
385 $first = "$others";
388 =for nobody
389 [ "$pattern" ] || continue
390 address="$address$first"
391 address="$address / $pattern /"
392 first=" $others"
394 =cut
397 print "\techo '$address ) { $action; }'\n";
398 return 1;
401 sub render_awkgen {
402 open(my $OUTPUT,'>',$_[0]);
403 my $root="CFGTEMP_$_[1]";
405 select $OUTPUT;
407 # initially change packages $4 and $5 to be able to correctly match repo based.
408 print "echo '{'\n";
409 print "echo '\trepo=\$4 ;'\n";
410 print "echo '\tpkg=\$5 ;'\n";
411 print "echo '\t\$5 = \$4 \"/\" \$5 ;'\n";
412 print "echo '\t\$4 = \"placeholder\" ;'\n";
413 print "echo '}'\n";
415 render_awkgen_folder($::FOLDER{$root});
417 # ... restore $4 and $5, and print the resulting line
418 print "echo '\n{'\n";
419 print "echo '\t\$4=repo ;'\n";
420 print "echo '\t\$5=pkg ;'\n";
421 print "echo '\tprint ;'\n";
422 print "echo '}'\n";
424 select STDOUT;
425 close($OUTPUT);
428 sub render_awkgen_folder {
429 my ($folder) = @_;
430 for (@{$folder->{children}}) {
431 if (/^CFGTEMP/) {
432 render_awkgen_folder($::FOLDER{$_});
433 } else {
434 my $module=$::MODULE{$_};
435 if ($module->{kind} == CHOICE) {
436 my %options;
438 # the list of options
439 for (@{ $module->{options} }) {
440 my $option = $_;
441 my @array=("\"\$$module->{var}\" == $_->{option}");
442 $options{$_->{option}} = \@array;
444 # and their implyed options
445 for (@{ $module->{options} }) {
446 my $option = $_;
447 for (@{exists $option->{imply}? $option->{imply} : [] }) {
448 push @{$options{$_}},
449 "\"\$$module->{var}\" == $option->{option}";
453 print "\n";
454 # and finally, render.
455 for (@{ $module->{options} }) {
456 print "if [ " . join(' -o ',@{ $options{ $_->{option} }}). " ]; then\n";
458 open(my $FILE,'<',$_->{file});
459 my $hasrules=0;
460 while(<$FILE>) {
461 next if /^#/;
462 next if /^\s*$/;
463 pkgsel_parse($1,$2) if m/^([^\s]+)\s+(.*)\s*\n?$/i;
464 $hasrules=1;
466 close($FILE);
467 print "\ttrue\n" unless $hasrules;
469 print "fi\n";
471 } else {
472 print "\nif [ \"\$$module->{var}\" == 1 ]; then\n";
473 open(my $FILE,'<',$module->{file});
474 my $hasrules=0;
475 while(<$FILE>) {
476 next if /^#/;
477 next if /^\s*$/;
478 pkgsel_parse($1,$2) if m/^([^\s]+)\s+(.*)\s*\n?$/i;
479 $hasrules=1;
481 close($FILE);
482 print "\ttrue\n" unless $hasrules;
483 print "fi\n";
489 sub render_rules_module {
490 my ($module,$offset) = @_;
491 my $var = $module->{var};
493 if ($module->{kind} == CHOICE) {
494 my $tmpvar = "CFGTEMP_$1" if $var =~ m/^SDECFG_(.*)/i;
495 my $listvar = "$tmpvar\_LIST";
496 my $defaultvar = "$tmpvar\_DEFAULT";
497 my $default = "undefined";
498 my $forcer;
500 $default = $module->{default} if exists $module->{default};
502 # initialize the list
503 print "${offset}$listvar=\n";
505 print "${offset}$defaultvar=$default\n";
506 print "${offset}\[ -n \"\$$var\" \] || $var=$default\n\n";
508 for ( @{ $module->{options} } ) {
509 my $option = $_;
510 (my $desc = $option->{desc}) =~ s/ /_/g;
512 # has something to force?
513 if (exists $option->{forced}) { $forcer = 1; }
515 if (exists $option->{deps}) {
516 print "${offset}if [ " .
517 join(' -a ', @{ $option->{deps} } ) .
518 " ]; then\n";
519 print "${offset}\t$listvar=\"\$$listvar $option->{option} $desc\"\n";
520 print "${offset}fi\n";
521 } else {
522 print "${offset}$listvar=\"\$$listvar $option->{option} $desc\"\n";
526 # enable the folder display
527 print "${offset}if \[ -n \"\$$listvar\" \]; then\n";
528 print "${offset}\t$module->{folder}=1\n";
529 print "${offset}else\n";
530 print "${offset}\tunset $module->{var}\n";
531 print "${offset}fi\n";
533 # has something to force?
534 if ($forcer) {
535 print "\n${offset}case \"\$$var\" in\n";
536 for ( @{ $module->{options} } ) {
537 my $option = $_;
538 if (exists $option->{forced}) {
539 print "${offset}\t$option->{option})\n";
540 for ( @{ $option->{forced} } ) {
541 print "$offset\t\t$_\n";
542 print "$offset\t\tSDECFGSET_$1\n" if $_ =~ m/^SDECFG_(.*)/i;
544 print "${offset}\t\t;;\n";
547 print "${offset}esac\n";
550 # printref($var,$module,$offset);
551 } elsif ($module->{kind} == ASK) {
552 my $default=0;
553 $default = $module->{default} if exists $module->{default};
555 #enable the folder display
556 print "$offset$module->{folder}=1\n";
558 # and set the default value if none is set.
559 print "$offset\[ -n \"\$$var\" \] || $var=$default\n";
561 # if enabled, append pkgsel and force the forced
563 if (exists $module->{forced}) {
564 print "\n${offset}if [ \"\$$var\" == 1 ]; then\n";
565 for ( @{ $module->{forced} } ) {
566 print "$offset\t$_\n";
567 print "$offset\tSDECFGSET_$1\n" if $_ =~ m/^SDECFG_(.*)/i;
569 print $offset."fi\n";
571 } else {
572 # just enable the feature
573 print "$offset$var=1\n";
575 # forced list doesn't make sense for {kind} == ALL
579 sub render_rules_nomodule {
580 my ($module,$offset) = @_;
581 my $var = $module->{var};
583 # unset the choice list, and the var
584 if ($module->{kind} == CHOICE) {
585 my $listvar = "CFGTEMP_$1_LIST" if $var =~ m/^SDECFG_(.*)/i;
586 print "${offset}unset $listvar\n";
588 print "${offset}unset SDECFGSET_$1\n" if $var =~ m/^SDECFG_(.*)/i;
589 print "${offset}unset $var\n";
592 sub render_rules {
593 open(my $FILE,'>',$_[0]);
594 my $root="CFGTEMP_$_[1]";
595 select $FILE;
597 # clean folder enablers
598 print "#\n# folder enablers\n#\n\n";
599 for (@$::FOLDERS) { print "$_=\n" unless /^$root$/; }
601 # pkgsel list
602 for (@$::MODULES) {
603 if (exists $::MODULE{$_}) {
604 my $module = $::MODULE{$_};
605 print "\n#\n# $module->{var} ("
606 . ($module->{kind} == ALL ? "ALL" : ($module->{kind} == ASK ? "ASK" : "CHOICE" ) )
607 . ")\n#\n";
610 if (exists $module->{deps}) {
611 print "if [ " . join(' -a ', @{ $module->{deps} } ) . " ]; then\n";
612 render_rules_module($module,"\t");
613 print "else\n";
614 render_rules_nomodule($module,"\t");
615 print "fi\n";
616 } else {
617 render_rules_module($module,"");
622 print "\n#\n# enable folder with enabled subfolders\n#\n";
623 for (@$::FOLDERS) {
624 my $folder = $::FOLDER{$_};
625 my @subdirs = grep(/^CFGTEMP/,@{$folder->{children}});
626 if ( @subdirs ) {
627 print "if [ -n \"\$".join('$', @subdirs )."\" ]; then\n";
628 print "\t$folder->{var}=1\n";
629 print "fi\n";
633 select STDOUT;
634 close($FILE);
637 # print the content of a hash
638 sub printref {
639 my ($name,$ref,$offset) = @_;
640 my $typeof = ref($ref);
642 print "$offset$name:";
643 if ($typeof eq '') {
644 print " '$ref'\n";
645 } elsif ($typeof eq 'HASH') {
646 print "\n";
647 for (sort keys %{ $ref }) {
648 printref($_,$ref->{$_},"$offset\t");
650 } elsif ($typeof eq 'ARRAY') {
651 my $i=0;
652 print "\n";
653 for (@{ $ref }) {
654 printref("[$i]",$_,"$offset\t");
655 $i++;
657 } else {
658 print " -> $typeof\n";
662 if ($#ARGV != 4) {
663 print "Usage mnemosyne.pl: <pkgseldir> <prefix> <configfile> <rulesfile> <awkgenerator>\n";
664 exit (1);
667 $| = 1;
669 $::ROOT=$ARGV[0];
670 scandir($ARGV[0],$ARGV[1]);
671 process_modules();
672 process_folders();
673 render_rules($ARGV[3],$ARGV[1]);
674 render_widgets($ARGV[2],$ARGV[1]);
675 render_awkgen($ARGV[4],$ARGV[1]);