Non-greedy matching for votestrings.
[usenet/usevote.git] / UVmenu.pm
1 # UVmenu: menu for interaction with the votetaker
2 # Used by uvvote.pl, uvcfv.pl, uvcount.pl
3  
4 package UVmenu;
5  
6 use strict;
7 use UVconfig;
8 use UVmessage;
9 use UVrules;
10 use vars qw($VERSION);
11
12 use Text::Wrap qw(wrap $columns);
13  
14 # Module version
15 $VERSION = "0.4";
16
17 ##############################################################################
18 # Menu for interaction with the votetaker                                    #
19 # Parameters: votes list and header (references to arrays)                   #
20 #             Body, Mailadress, Name, Ballot ID,                             #
21 #             Voting (references to strings)                                 #
22 #             List of newly set fields (reference to array)                  #
23 #             List of errors to correct (Array-Ref)                          #
24 # Return Values: 'w': proceed                                                #
25 #                'i': ignore (don't save vote)                               #
26 ##############################################################################
27
28 sub menu {
29   my ($votes, $header, $body, $addr, $name, $ballot_id, $voting, $set, $errors) = @_;
30   my $input = "";
31   my $voter_addr = $$addr || '';
32   my $voter_name = $$name || '';
33   my @newvotes = @$votes;
34   my $mailonly = 0;
35   my %errors;
36   $$ballot_id ||= '';
37
38   foreach my $error (@$errors) {
39
40     # unrecognized vote: extract group number und display warning
41     if ($error =~ /^UnrecognizedVote #(\d+)#(.+)$/) {
42       $errors{UnrecognizedVote} ||= UVmessage::get("MENU_UNRECOGNIZEDVOTE");
43       $errors{UnrecognizedVote} .= "\n  " . UVmessage::get("MENU_UNRECOGNIZED_LIST")
44                                           . " #$1: $2";
45
46     # violated rule: extract rule number and display warning
47     } elsif ($error =~ /^ViolatedRule #(\d+)$/) {
48       $errors{ViolatedRule} ||= UVmessage::get("MENU_VIOLATEDRULE", (RULE => "#$1"));
49
50     } else {
51       # special handling if called from uvballot.pl
52       $mailonly = 1 if ($error =~ s/Ballot$//);
53
54       # get error message for this error from messages.cfg
55       $errors{$error} = UVmessage::get("MENU_" . uc($error));
56     }
57   }
58
59   # This loop is only left by 'return'
60   while (1) {
61
62     system($config{clearcmd});
63     print "-> $config{votename} <-\n";
64     print UVmessage::get("MENU_PROBLEMS") . "\n";
65
66     foreach my $error (keys %errors) {
67       print "* $errors{$error}\n";
68     }
69
70     my $menucaption = UVmessage::get("MENU_CAPTION");
71     print "\n\n$menucaption\n";
72     print "=" x length($menucaption), "\n\n";
73
74     # don't print this option if called from uvcfv.pl
75     unless ($mailonly) {
76       print "(0) ", UVmessage::get("MENU_DIFF_BALLOT"), "\n";
77     }
78
79     print "(1) ", UVmessage::get("MENU_SHOW_MAIL"), "\n\n",
80           UVmessage::get("MENU_CHANGE_PROPERTIES"), "\n",
81           "(2) ", UVmessage::get("MENU_ADDRESS"), " [$voter_addr]\n";
82
83     # don't print these options if called from uvcfv.pl
84     unless ($mailonly) {
85       print "(3) ", UVmessage::get("MENU_NAME"), " [$voter_name]\n";
86       print "(4) ", UVmessage::get("MENU_VOTES"), " [", @$votes, "]\n";
87       print "(5) ", UVmessage::get("MENU_BALLOT_ID"), " [$$ballot_id]\n"
88         if ($config{personal});
89       print "(6) ", UVmessage::get("MENU_BDSG"), "\n" if ($config{bdsg});
90       print "(7) ", UVmessage::get("MENU_VOTING"), " [", $$voting, "]\n";
91     }
92
93     print "\n",
94           "(i) ", UVmessage::get("MENU_IGNORE"), "\n",
95           "(w) ", UVmessage::get("MENU_PROCEED"), "\n\n",
96           UVmessage::get("MENU_PROMPT");
97
98     do { $input = <STDIN>; } until ($input);
99     chomp $input;
100     print "\n";
101
102     # only accept 1, 2, i and w if called from uvcfv.pl
103     next if ($mailonly && $input !~ /^[12iw]$/i);
104
105     if ($input eq '0') {
106       # ignore SIGPIPE (Bug in more and less)
107       $SIG{PIPE} = 'IGNORE';
108       open (DIFF, "|$config{diff} - $config{sampleballotfile} | $config{pager}");
109       print DIFF $$body, "\n";
110       close (DIFF);
111
112     } elsif ($input eq '1') {
113       system($config{clearcmd});
114       # ignore SIGPIPE (Bug in more and less)
115       $SIG{PIPE} = 'IGNORE';
116       open (MORE, "|$config{pager}");
117       print MORE join("\n", @$header), "\n\n", $$body, "\n";
118       close (MORE);
119       
120       print "\n", UVmessage::get("MENU_GETKEY");
121       $input = <STDIN>;
122
123     } elsif ($input eq '2') {
124       my $sel;
125       do {
126         print "[a] ", UVmessage::get("MENU_ADDRESS_OK"), "\n",
127               "[b] ", UVmessage::get("MENU_ADDRESS_CHANGE"), "\n",
128               "[c] ", UVmessage::get("MENU_ADDRESS_INVALID"), "\n\n",
129               UVmessage::get("MENU_PROMPT");
130         $sel = <STDIN>;
131       } until ($sel =~ /^[abc]$/i);
132       if ($sel =~ /^a$/i) {
133         delete $errors{SuspiciousAccount};
134         delete $errors{InvalidAddress};
135         next;
136       } elsif ($sel =~ /^c$/i) {
137         delete $errors{SuspiciousAccount};
138         $errors{InvalidAddress} = UVmessage::get("MENU_INVALIDADDRESS") . " " .
139                                   UVmessage::get("MENU_INVALIDADDRESS2");
140         next;
141       }
142         
143       do {
144         print "\n", UVmessage::get("MENU_ADDRESS_PROMPT"), " ";
145         $voter_addr = <STDIN>;
146         chomp ($voter_addr);
147       } until ($voter_addr);
148       $$addr = $voter_addr;
149       push (@$set, 'Adresse');
150       delete $errors{SuspiciousAccount};
151       delete $errors{InvalidAddress};
152       check_ballotid(\%errors, \$voter_addr, $ballot_id, \%ids);
153
154     } elsif ($input eq '3') {
155       my $sel;
156       do {
157         print "[a] ", UVmessage::get("MENU_NAME_OK"), "\n",
158               "[b] ", UVmessage::get("MENU_NAME_CHANGE"), "\n",
159               "[c] ", UVmessage::get("MENU_NAME_INVALID"), "\n\n",
160               UVmessage::get("MENU_PROMPT");
161         $sel = <STDIN>;
162       } until ($sel =~ /^[abc]$/i);
163       if ($sel =~ /^a$/i) {
164         delete $errors{InvalidName};
165         next;
166       } elsif ($sel =~ /^c$/i) {
167         $errors{InvalidName} = UVmessage::get("MENU_INVALIDNAME");
168         next;
169       }
170       print UVmessage::get("MENU_NAME"), ": ";
171       $voter_name = <STDIN>;
172       chomp ($voter_name);
173       $$name = $voter_name;
174       push (@$set, 'Name');
175       delete $errors{NoName};
176       delete $errors{InvalidName};
177
178       $errors{InvalidName} = UVmessage::get("MENU_INVALIDNAME")
179         unless ($voter_name =~ /$config{name_re}/);
180  
181     } elsif ($input eq '4') {
182       # set votes
183
184       my $sel;
185       do {
186         print "[a] ", UVmessage::get("MENU_VOTES_OK"), "\n",
187               "[b] ", UVmessage::get("MENU_VOTES_RESET"), "\n",
188               "[c] ", UVmessage::get("MENU_VOTES_INVALID"), "\n",
189               "[d] ", UVmessage::get("MENU_VOTES_CANCELLED"), "\n\n",
190               UVmessage::get("MENU_PROMPT");
191         $sel = <STDIN>;
192       } until ($sel =~ /^[abcd]$/i);
193       if ($sel =~ /^[ad]$/i) {
194         delete $errors{NoVote};
195         delete $errors{UnrecognizedVote};
196         delete $errors{ViolatedRule};
197         delete $errors{DuplicateVote};
198         if ($sel =~ /^d$/i) {
199           # cancelled vote: replace all votes with an A
200           @$votes = split(//, 'A' x scalar @groups);
201           push @$set, 'Stimmen';
202           # some errors are irrelevant when cancelling a vote:
203           delete $errors{InvalidName};
204           delete $errors{NoName};
205           delete $errors{InvalidBDSG};
206           delete $errors{InvalidAddress};
207           delete $errors{SuspiciousAccount};
208         }
209         next;
210       } elsif ($sel =~ /^c$/i) {
211         $errors{NoVote} = UVmessage::get("MENU_INVALIDVOTE");
212         next;
213       }
214
215       # Set columns for Text::Wrap
216       $columns = $config{rightmargin};
217       print "\n", wrap('', '', UVmessage::get("MENU_VOTES_REENTER_ASK")), "\n\n";
218       print UVmessage::get("MENU_VOTES_REENTER_LEGEND"), "\n";
219
220       for (my $n=0; $n<@groups; $n++) {
221         my $voteinput = "";
222         $votes->[$n] ||= 'E';
223
224         # repeat while invalid character entered
225         while (!($voteinput =~ /^[JNE]$/)) {
226           my $invalid = $#groups ? 0 : 1;
227           print UVmessage::get("MENU_VOTES_REENTER", (GROUP => $groups[$n]));
228           $voteinput = <STDIN>;
229           chomp $voteinput;
230           $voteinput ||= $votes->[$n];
231           $voteinput =~ tr/jne/JNE/;
232         }
233         
234         # valid input, save new votes
235         $newvotes[$n] = $voteinput;
236       } 
237
238       print "\n\n";
239       my $oldvotes = UVmessage::get("MENU_VOTES_REENTER_OLD");
240       my $newvotes = UVmessage::get("MENU_VOTES_REENTER_NEW");
241       my $oldlen = length($oldvotes);
242       my $newlen = length($newvotes);
243       my $maxlen = 1 + (($newlen>$oldlen) ? $newlen : $oldlen);
244       print $oldvotes, ' ' x ($maxlen - length($oldvotes)), @$votes, "\n",
245             $newvotes, ' ' x ($maxlen - length($newvotes)), @newvotes, "\n\n";
246
247       do {
248         print "[a] ", UVmessage::get("MENU_VOTES_REENTER_ACK"), "    ",
249               "[b] ", UVmessage::get("MENU_VOTES_REENTER_NACK"), "\n\n", 
250                UVmessage::get("MENU_PROMPT");
251         $sel = <STDIN>;
252       } until ($sel =~ /^[ab]$/i);
253
254       next if ($sel =~ /^b$/i);
255       @$votes = @newvotes;
256       push @$set, 'Stimmen';
257       delete $errors{UnrecognizedVote};
258       delete $errors{DuplicateVote};
259       delete $errors{NoVote};
260       delete $errors{ViolatedRule};
261
262       if (my $rule = UVrules::rule_check($votes)) {
263         $errors{ViolatedRule} = UVmessage::get("MENU_VIOLATEDRULE", (RULE => "#$rule"));
264       }
265
266     } elsif ($input eq '5' && $config{personal}) {
267       print "\n", UVmessage::get("MENU_BALLOT_ID"), ": ";
268       $$ballot_id = <STDIN>;
269       chomp ($$ballot_id);
270       push (@$set, 'Kennung');
271       check_ballotid(\%errors, \$voter_addr, $ballot_id, \%ids);
272
273     } elsif ($input eq '6' && $config{bdsg}) {
274       my $sel;
275       do {
276         print "[a] ", UVmessage::get("MENU_BDSG_ACCEPTED"), "\n",
277               "[b] ", UVmessage::get("MENU_BDSG_DECLINED"), "\n\n",
278               UVmessage::get("MENU_PROMPT");
279         $sel = <STDIN>;
280       } until ($sel =~ /^[ab]$/i);
281
282       if ($sel =~ /^a$/i) {
283         delete $errors{InvalidBDSG};
284       } else {
285         $errors{InvalidBDSG} = UVmessage::get("MENU_INVALIDBDSG");
286       }
287
288     } elsif ($input eq '7') {
289       my $sel;
290       do {
291         print "[a] ", UVmessage::get("MENU_VOTING_CORRECT"), "\n",
292               "[b] ", UVmessage::get("MENU_VOTING_WRONG"), "\n\n",
293               UVmessage::get("MENU_PROMPT");
294         $sel = <STDIN>;
295       } until ($sel =~ /^[ab]$/i);
296
297       if ($sel =~ /^a$/i) {
298         delete $errors{NoVoting};
299         delete $errors{WrongVoting};
300       } else {
301         $errors{WrongVoting} = UVmessage::get("MENU_WRONGVOTING");
302       }
303
304     } elsif ($input =~ /^i$/i) {
305       my $ignore = UVmessage::get("MENU_IGNORE_STRING");
306       # Set columns for Text::Wrap
307       $columns = $config{rightmargin};
308       print wrap('', '', UVmessage::get("MENU_IGNORE_WARNING",
309                                         (MENU_IGNORE_STRING => $ignore)
310                                        ));
311       if (<STDIN> eq "$ignore\n") {
312         print "\n";
313         return "i";
314       }
315
316     } elsif ($input =~ /^w$/i) {
317
318       if (keys %errors) {
319         if ((keys %errors)==1 && $errors{UnrecognizedVote}) {
320           # unrecognized vote lines aren't errors if votetaker
321           # did not change them
322           @$errors = ();
323         } else {
324           # Set columns for Text::Wrap
325           $columns = $config{rightmargin};
326           @$errors = keys %errors;
327           my $warning = ' ' . UVmessage::get("MENU_ERROR_WARNING") . ' ';
328           my $length = length($warning);
329           print "\n", '*' x (($config{rightmargin}-$length)/2), $warning,
330                 '*' x (($config{rightmargin}-$length)/2), "\n\n",
331                 wrap('', '', UVmessage::get("MENU_ERROR_TEXT")), "\n\n",
332                 '*' x $config{rightmargin}, "\n\n",
333                 UVmessage::get("MENU_ERROR_GETKEY");
334           my $input = <STDIN>;
335           next if ($input !~ /^y$/i);
336           print "\n";
337         }
338       } else {
339         @$errors = ();
340       }
341  
342       system($config{clearcmd});
343       print "\n", UVmessage::get("MENU_PROCESSING"), "\n";
344       return "w";
345     }
346   }
347
348   sub check_ballotid {
349     my ($errors, $voter_addr, $ballot_id, $ids) = @_;
350
351     return 0 unless ($config{personal});
352
353     delete $errors->{NoBallotID};
354     delete $errors->{WrongBallotID};
355     delete $errors->{AddressNotRegistered};
356
357     if ($$ballot_id) {
358       if ($ids->{$$voter_addr}) {
359         if ($ids->{$$voter_addr} ne $$ballot_id) {
360           # ballot id incorrect
361           $errors->{WrongBallotID} = UVmessage::get("MENU_WRONGBALLOTID");
362         }
363       } else {
364         $errors->{AddressNotRegistered} = UVmessage::get("MENU_ADDRESSNOTREGISTERED");
365       } 
366     } else {
367       $errors->{NoBallotID} = UVmessage::get("MENU_NOBALLOTID");
368     }
369   }
370
371 }
372
373
374 ##############################################################################
375 # Menu for sorting out duplicate votings manually                            #
376 # Parameters: References to hashes with the paragraphs from the result file  #
377 #             and the default value                                          #
378 # Return value: selected menu item (1, 2 or 0)                               #
379 ##############################################################################
380
381 sub dup_choice {
382   my ($vote1, $vote2, $default) = @_;
383
384   print STDERR "\n", UVmessage::get("MENU_DUP_VOTE"), "\n\n";
385   print STDERR UVmessage::get("MENU_DUP_FIRST"), "\n";
386   print STDERR "A: $vote1->{A}\n";
387   print STDERR "N: $vote1->{N}\n";
388   print STDERR "D: $vote1->{D}\n";
389   print STDERR "K: $vote1->{K}\n";
390   print STDERR "S: $vote1->{S}\n\n";
391   print STDERR UVmessage::get("MENU_DUP_SECOND"), "\n";
392   print STDERR "A: $vote2->{A}\n";
393   print STDERR "N: $vote2->{N}\n";
394   print STDERR "D: $vote2->{D}\n";
395   print STDERR "K: $vote2->{K}\n";
396   print STDERR "S: $vote2->{S}\n\n";
397   print STDERR "1: ", UVmessage::get("MENU_DUP_DELFIRST"), "\n",
398                "2: ", UVmessage::get("MENU_DUP_DELSECOND"), "\n",
399                "0: ", UVmessage::get("MENU_DUP_DELNONE"), "\n\n";
400
401   my $input;
402
403   do {
404     print STDERR UVmessage::get("MENU_PROMPT"), "[$default] ";
405     $input = <STDIN>;
406     chomp $input;
407   } until ($input eq '' || ($input >= 0 && $input<3));
408
409   return $input || $default;
410 }
411
412 1;
This page took 0.020221 seconds and 3 git commands to generate.