Parent Directory
|
Revision Log
Update quiz prefix for arrays. This is not a complete fix, but it gets incrementally closer to a fix.
1 # This file provided the fundamental macros for the pg language 2 # These macros define the interface between the problems written by 3 # the professor and the processing which occurs in the script 4 # processProblem.pl 5 6 7 BEGIN { 8 be_strict(); 9 } 10 11 sub _PG_init{ 12 13 } 14 15 #package PG; 16 17 18 =head1 NAME 19 20 PG.pl --- located in the courseScripts directory. 21 Defines the Program Generating language at the most basic level. 22 23 =head1 SYNPOSIS 24 25 The basic PG problem structure: 26 27 DOCUMENT(); # should be the first statment in the problem 28 loadMacros(.....); # (optional) load other macro files if needed. 29 # (loadMacros is defined in F<dangerousMacros.pl>) 30 31 HEADER_TEXT(...); # (optional) used only for inserting javaScript into problems. 32 33 # # insert text of problems 34 TEXT("Problem text to be", 35 "displayed. Enter 1 in this blank:", 36 ANS_RULE(1,30) # ANS_RULE() defines an answer blank 30 characters long. 37 # It is defined in F<PGbasicmacros.pl> 38 ); 39 40 41 ANS( answer_evalutors); # see F<PGanswermacros.pl> for examples of answer evaluatiors. 42 43 ENDDOCUMENT() # must be the last statement in the problem 44 45 46 47 =head1 DESCRIPTION 48 49 As described in the synopsis, this file and the macros C<DOCUMENT()> and C<ENDDOCUMENT()> determine 50 the interface between problems written in the PG language and the rest of B<WeBWorK>, in particular 51 the subroutine C<createPGtext(()> in the file F<translate.pl>. 52 53 C<DOCUMENT()> must be the first statement in each problem template. 54 It initializes variables, 55 in particular all of the contents of the 56 environment variable become defined in the problem enviroment. 57 (See 58 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html>) 59 60 ENDDOCUMENT() must the last executable statement in any problem template. It returns 61 the rendered problem, answer evaluators and other flags to the rest of B<WeBWorK>, specificially 62 to the routine C<createPGtext()> defined in F<translate.pl> 63 64 65 The C<HEADER_TEXT()>, C<TEXT()>, and C<ANS()> functions load the 66 header text string, the problem text string. 67 and the answer evaulator queue respectively. 68 69 70 =cut 71 72 73 # Private variables for the PG.pl file. 74 75 my ($STRINGforOUTPUT, $STRINGforHEADER_TEXT, @PG_ANSWERS, @PG_UNLABELED_ANSWERS); 76 my %PG_ANSWERS_HASH ; 77 our $PG_STOP_FLAG; 78 79 # my variables are unreliable if two DOCUMENTS were to be called before an ENDDOCUMENT 80 # there could be conflicts. As I understand the behavior of the Apache child 81 # this cannot occur -- a child finishes with one request before obtaining the next 82 83 # DOCUMENT must come early in every .pg file, before any answers or text are 84 # defined. It initializes the variables. 85 # It can appear only once. 86 87 =head2 DOCUMENT() 88 89 C<DOCUMENT()> must be the first statement in each problem template. It can 90 only be used once in each problem. 91 92 C<DOCUMENT()> initializes some empty variables and via C<INITIALIZE_PG()> unpacks the 93 variables in the C<%envir> variable which is implicitly passed to the problem. It must 94 be the first statement in any problem template. It 95 also unpacks any answers submitted and places them in the C<@submittedAnswer> list, 96 saves the problem seed in C<$PG_original_problemSeed> in case you need it later, and 97 initializes the pseudo random number generator object in C<$PG_random_generator>. 98 99 You can reset the standard number generator using the command: 100 101 $PG_random_generator->srand($new_seed_value); 102 103 (See also C<SRAND> in the L<PGbasicmacros.pl> file.) 104 105 The 106 environment variable contents is defined in 107 L</webwork_system_html/docs/techdescription/pglanguage/PGenvironment.html> 108 109 110 =cut 111 112 sub DOCUMENT { 113 114 $STRINGforOUTPUT =""; 115 $STRINGforHEADER_TEXT =""; 116 @PG_ANSWERS=(); 117 $PG_STOP_FLAG=0; 118 @PG_UNLABELED_ANSWERS = (); 119 %PG_ANSWERS_HASH = (); 120 # FIXME: We are initializing these variables into both Safe::Root1 (the cached safe compartment) 121 # and Safe::Root2 (the current one) 122 # There is a good chance they won't be properly updated in one or the other of these compartments. 123 124 # @main::PG_ANSWER_ENTRY_ORDER = (); 125 # $main::ANSWER_PREFIX = 'AnSwEr'; 126 # %main::PG_FLAGS=(); #global flags 127 # $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers ); 128 # $main::showHint = 1 unless defined($main::showHint); 129 # $main::solutionExists =0; 130 # $main::hintExists =0; 131 # %main::gifs_created = (); 132 eval(q! 133 @main::PG_ANSWER_ENTRY_ORDER = (); 134 $main::ANSWER_PREFIX = 'AnSwEr'; 135 %main::PG_FLAGS=(); #global flags 136 $main::showPartialCorrectAnswers = 0 unless defined($main::showPartialCorrectAnswers ); 137 $main::showHint = 1 unless defined($main::showHint); 138 $main::solutionExists =0; 139 $main::hintExists =0; 140 $main::pgComment = ''; 141 %main::gifs_created = (); 142 143 !); 144 # warn eval(q! "PG.pl: The envir variable $main::{envir} is".join(" ",%main::envir)!); 145 my $rh_envir = eval(q!\%main::envir!); 146 my %envir = %$rh_envir; 147 148 # Save the file name for use in error messages 149 my ($callpkg,$callfile) = caller(0); 150 $envir{__files__}{$callfile} = $envir{templateDirectory}.$envir{fileName}; 151 152 #no strict; 153 foreach my $var (keys %envir) { 154 eval(q!$main::!.$var.q! = $main::envir{!.$var.q!}! ); #whew!! makes sure $var is interpolated but $main:: is evaluated at run time. 155 # warn eval(q! "var $var is defined ". $main::!.$var); 156 warn "Problem defining ", q{\$main::}.$var, " while initializing the PG problem: $@" if $@; 157 } 158 #use strict; 159 #FIXME these strict pragmas don't seem to be needed and they cause trouble in perl 5.6.0 160 161 162 163 eval(q! 164 @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers); 165 $main::PG_original_problemSeed = $main::problemSeed; 166 $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator."; 167 $main::ans_rule_count = 0; # counts questions 168 169 # end unpacking of environment variables. 170 $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX) 171 172 !); 173 # @main::submittedAnswers = @{$main::refSubmittedAnswers} if defined($main::refSubmittedAnswers); 174 # $main::PG_original_problemSeed = $main::problemSeed; 175 # $main::PG_random_generator = new PGrandom($main::problemSeed) || die "Can't create random number generator."; 176 # $main::ans_rule_count = 0; # counts questions 177 178 # end unpacking of environment variables. 179 # $main::QUIZ_PREFIX = '' unless defined($main::QUIZ_PREFIX) 180 181 if ($main::envir{displayMode} eq 'HTML_jsMath') { 182 my $prefix = ""; 183 if (!$main::envir{jsMath}{reportMissingFonts}) { 184 $prefix .= '<SCRIPT>noFontMessage = 1</SCRIPT>'."\n"; 185 } elsif ($main::envir{jsMath}{missingFontMessage}) { 186 $prefix .= '<SCRIPT>missingFontMessage = "'.$main::envir{jsMath}{missingFontMessage}.'"</SCRIPT>'."\n"; 187 } 188 $prefix .= '<SCRIPT>processDoubleClicks = '.($main::envir{jsMath}{processDoubleClicks}?'1':'0')."</SCRIPT>\n"; 189 $STRINGforOUTPUT = 190 $prefix . 191 '<SCRIPT SRC="'.$main::envir{jsMathURL}.'"></SCRIPT>' . "\n" . 192 '<NOSCRIPT><CENTER><FONT COLOR="#CC0000">' . 193 '<B>Warning: the mathematics on this page requires JavaScript.<BR>' . 194 'If your browser supports it, be sure it is enabled.</B>'. 195 '</FONT></CENTER><p></NOSCRIPT>' . 196 $STRINGforOUTPUT; 197 $STRINGforOUTPUT .= 198 '<SCRIPT>jsMath.Setup.Script("plugins/noImageFonts.js")</SCRIPT>' 199 if ($main::envir{jsMath}{noImageFonts}); 200 } 201 202 $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{asciimathURL}.'"></SCRIPT>' . "\n" . 203 '<SCRIPT>mathcolor = "black"</SCRIPT>' . $STRINGforOUTPUT 204 if ($main::envir{displayMode} eq 'HTML_asciimath'); 205 206 $STRINGforOUTPUT = '<SCRIPT SRC="'.$main::envir{LaTeXMathMLURL}.'"></SCRIPT>'."\n" . $STRINGforOUTPUT 207 if ($main::envir{displayMode} eq 'HTML_LaTeXMathML'); 208 209 } 210 211 sub inc_ans_rule_count { 212 eval(q!++$main::ans_rule_count!); # evalute at runtime to get correct main:: 213 } 214 # HEADER_TEXT is for material which is destined to be placed in the header of the html problem -- such 215 # as javaScript code. 216 217 =head2 HEADER_TEXT() 218 219 220 HEADER_TEXT("string1", "string2", "string3"); 221 222 The C<HEADER_TEXT()> 223 function concatenates its arguments and places them in the output 224 header text string. It is used for material which is destined to be placed in 225 the header of the html problem -- such as javaScript code. 226 It can be used more than once in a file. 227 228 229 =cut 230 231 sub HEADER_TEXT { 232 my @in = @_; 233 $STRINGforHEADER_TEXT .= join(" ",@in); 234 } 235 236 # TEXT is the function which defines text which will appear in the problem. 237 # All text must be an argument to this function. Any other statements 238 # are calculations (done in perl) which will not directly appear in the 239 # output. Think of this as the "print" function for the .pg language. 240 # It can be used more than once in a file. 241 242 =head2 TEXT() 243 244 TEXT("string1", "string2", "string3"); 245 246 The C<TEXT()> function concatenates its arguments and places them in the output 247 text string. C<TEXT()> is the function which defines text which will appear in the problem. 248 All text must be an argument to this function. Any other statements 249 are calculations (done in perl) which will not directly appear in the 250 output. Think of this as the "print" function for the .pg language. 251 It can be used more than once in a file. 252 253 =cut 254 255 sub TEXT { 256 return "" if $PG_STOP_FLAG; 257 my @in = @_; 258 $STRINGforOUTPUT .= join(" ",@in); 259 } 260 261 =head2 STOP_RENDERING() 262 263 STOP_RENDERING() unless all_answers_are_correct; 264 265 No text is printed and no answer blanks or answer evaluators are stored or processed until 266 RESUME_RENDERING() is executed. 267 268 =cut 269 270 sub STOP_RENDERING { 271 $PG_STOP_FLAG=1; 272 ""; 273 } 274 275 =head2 RESUME_RENDERING() 276 277 RESUME_RENDERING(); 278 279 Resumes processing of text, answer blanks, and 280 answer evaluators. 281 282 =cut 283 284 sub RESUME_RENDERING { 285 $PG_STOP_FLAG=0; 286 ""; 287 } 288 289 =head2 ANS() 290 291 ANS(answer_evaluator1, answer_evaluator2, answer_evaluator3,...) 292 293 Places the answer evaluators in the unlabeled answer_evaluator queue. They will be paired 294 with unlabeled answer rules (answer entry blanks) in the order entered. This is the standard 295 method for entering answers. 296 297 LABELED_ANS(answer_evaluater_name1, answer_evaluator1, answer_evaluater_name2,answer_evaluator2,...) 298 299 Places the answer evaluators in the labeled answer_evaluator hash. This allows pairing of 300 labeled answer evaluators and labeled answer rules which may not have been entered in the same 301 order. 302 303 =cut 304 305 sub ANS{ # store answer evaluators which have not been explicitly labeled 306 return "" if $PG_STOP_FLAG; 307 my @in = @_; 308 while (@in ) { 309 warn("<BR><B>Error in ANS:$in[0]</B> -- inputs must be references to 310 subroutines<BR>") 311 unless ref($in[0]); 312 push(@PG_ANSWERS, shift @in ); 313 } 314 } 315 sub LABELED_ANS { #a better alias for NAMED_ANS 316 &NAMED_ANS; 317 } 318 319 sub NAMED_ANS{ # store answer evaluators which have been explicitly labeled (submitted in a hash) 320 return "" if $PG_STOP_FLAG; 321 my @in = @_; 322 while (@in ) { 323 my $label = shift @in; 324 $label = eval(q!$main::QUIZ_PREFIX.$label!); 325 my $ans_eval = shift @in; 326 TEXT("<BR><B>Error in NAMED_ANS:$in[0]</B> 327 -- inputs must be references to subroutines<BR>") 328 unless ref($ans_eval); 329 $PG_ANSWERS_HASH{$label}= $ans_eval; 330 } 331 } 332 sub RECORD_ANS_NAME { # this maintains the order in which the answer rules are printed. 333 return "" if $PG_STOP_FLAG; 334 my $label = shift; 335 eval(q!push(@main::PG_ANSWER_ENTRY_ORDER, $label)!); 336 $label; 337 } 338 339 sub NEW_ANS_NAME { # this keeps track of the answers which are entered implicitly, 340 # rather than with a specific label 341 return "" if $PG_STOP_FLAG; 342 my $number=shift; 343 my $prefix = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!); 344 my $label = $prefix.$number; 345 push(@PG_UNLABELED_ANSWERS,$label); 346 $label; 347 } 348 sub ANS_NUM_TO_NAME { # This converts a number to an answer label for use in 349 # radio button and check box answers. No new answer 350 # name is recorded. 351 my $number=shift; 352 my $label = eval(q!$main::QUIZ_PREFIX.$main::ANSWER_PREFIX!).$number; 353 $label; 354 } 355 356 my $vecnum; 357 358 sub RECORD_FORM_LABEL { # this stores form data (such as sticky answers), but does nothing more 359 # it's a bit of hack since we are storing these in the KEPT_EXTRA_ANSWERS queue even if they aren't answers per se. 360 return "" if $PG_STOP_FLAG; 361 my $label = shift; # the label of the input box or textarea 362 eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!); #put the labels into the hash to be caught later for recording purposes 363 $label; 364 } 365 sub NEW_ANS_ARRAY_NAME { # this keeps track of the answers which are entered implicitly, 366 # rather than with a specific label 367 return "" if $PG_STOP_FLAG; 368 my $number=shift; 369 $vecnum = 0; 370 my $row = shift; 371 my $col = shift; 372 # my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]"; 373 my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!); 374 push(@PG_UNLABELED_ANSWERS,$label); 375 $label; 376 } 377 378 sub NEW_ANS_ARRAY_NAME_EXTENSION { # this keeps track of the answers which are entered implicitly, 379 # rather than with a specific label 380 return "" if $PG_STOP_FLAG; 381 my $number=shift; 382 my $row = shift; 383 my $col = shift; 384 if( $row == 0 && $col == 0 ){ 385 $vecnum += 1; 386 } 387 #FIXME change made to conform to HTML 4.01 standards. "Name" attributes can only contain 388 # alphanumeric characters, _ : and . 389 # Also need to make corresponding changes in PGmorematrixmacros. grep for ArRaY. 390 #my $label = "ArRaY"."$number"."["."$vecnum".","."$row".","."$col"."]"; 391 my $label = eval(q!$main::QUIZ_PREFIX."ArRaY"."$number"."__"."$vecnum".":"."$row".":"."$col"."__"!); 392 eval(q!push(@main::KEPT_EXTRA_ANSWERS, $label)!);#put the labels into the hash to be caught later for recording purposes 393 $label; 394 } 395 396 397 sub get_PG_ANSWERS_HASH { 398 # update the PG_ANSWWERS_HASH, then report the result. 399 # This is used in writing sequential problems 400 # if there is an input, use that as a key into the answer hash 401 my $key = shift; 402 my (%pg_answers_hash, @pg_unlabeled_answers); 403 %pg_answers_hash= %PG_ANSWERS_HASH; 404 #warn "order ", eval(q!@main::PG_ANSWER_ENTRY_ORDER!); 405 #warn "pg answers", %PG_ANSWERS_HASH; 406 #warn "unlabeled", @PG_UNLABELED_ANSWERS; 407 my $index=0; 408 foreach my $label (@PG_UNLABELED_ANSWERS) { 409 if ( defined($PG_ANSWERS[$index]) ) { 410 $pg_answers_hash{"$label"}= $PG_ANSWERS[$index]; 411 #warn "recording answer label = $label"; 412 } else { 413 warn "No answer provided by instructor for answer $label"; 414 } 415 $index++; 416 } 417 if ($key) { 418 return $pg_answers_hash{$key}; 419 } else { 420 return %pg_answers_hash; 421 } 422 } 423 # ENDDOCUMENT must come at the end of every .pg file. 424 # It exports the resulting text of the problem, the text to be used in HTML header material 425 # (for javaScript), the list of answer evaluators and any other flags. It can appear only once and 426 # it MUST be the last statement in the problem. 427 428 =head2 ENDDOCUMENT() 429 430 ENDDOCUMENT() must the last executable statement in any problem template. It can 431 only appear once. It returns 432 an array consisting of 433 434 A reference to a string containing the rendered text of the problem. 435 A reference to a string containing text to be placed in the header 436 (for javaScript) 437 A reference to the array containing the answer evaluators. 438 (May be changed to a hash soon.) 439 A reference to an associative array (hash) containing various flags. 440 441 The following flags are set by ENDDOCUMENT: 442 (1) showPartialCorrectAnswers -- determines whether students are told which 443 of their answers in a problem are wrong. 444 (2) recordSubmittedAnswers -- determines whether students submitted answers 445 are saved. 446 (3) refreshCachedImages -- determines whether the cached image of the problem 447 in typeset mode is always refreshed (i.e. setting this to 1 means cached 448 images are not used). 449 (4) solutionExits -- indicates the existence of a solution. 450 (5) hintExits -- indicates the existence of a hint. 451 (6) comment -- contents of COMMENT commands if any. 452 (7) showHintLimit -- determines the number of attempts after which hint(s) will be shown 453 454 (8) PROBLEM_GRADER_TO_USE -- chooses the problem grader to be used in this order 455 (a) A problem grader specified by the problem using: 456 install_problem_grader(\&grader); 457 (b) One of the standard problem graders defined in PGanswermacros.pl when set to 458 'std_problem_grader' or 'avg_problem_grader' by the environment variable 459 $PG_environment{PROBLEM_GRADER_TO_USE} 460 (c) A subroutine referenced by $PG_environment{PROBLEM_GRADER_TO_USE} 461 (d) The default &std_problem_grader defined in PGanswermacros.pl 462 463 464 =cut 465 466 sub ENDDOCUMENT { 467 468 my $index=0; 469 foreach my $label (@PG_UNLABELED_ANSWERS) { 470 if ( defined($PG_ANSWERS[$index]) ) { 471 $PG_ANSWERS_HASH{"$label"}= $PG_ANSWERS[$index]; 472 #warn "recording answer label = $label"; 473 } else { 474 warn "No answer provided by instructor for answer $label"; 475 } 476 $index++; 477 } 478 479 $STRINGforOUTPUT .="\n"; 480 eval q{ #make sure that "main" points to the current safe compartment by evaluating these lines. 481 $main::PG_FLAGS{'showPartialCorrectAnswers'} = $main::showPartialCorrectAnswers; 482 $main::PG_FLAGS{'recordSubmittedAnswers'} = $main::recordSubmittedAnswers; 483 $main::PG_FLAGS{'refreshCachedImages'} = $main::refreshCachedImages; 484 $main::PG_FLAGS{'comment'} = $main::pgComment; 485 $main::PG_FLAGS{'hintExists'} = $main::hintExists; 486 $main::PG_FLAGS{'showHintLimit'} = $main::showHint; 487 $main::PG_FLAGS{'solutionExists'} = $main::solutionExists; 488 $main::PG_FLAGS{ANSWER_ENTRY_ORDER} = \@main::PG_ANSWER_ENTRY_ORDER; 489 $main::PG_FLAGS{KEPT_EXTRA_ANSWERS} = \@main::KEPT_EXTRA_ANSWERS;##need to keep array labels that don't call "RECORD_ANS_NAME" 490 $main::PG_FLAGS{ANSWER_PREFIX} = $main::ANSWER_PREFIX; 491 # install problem grader 492 if (defined($main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) ) { 493 # problem grader defined within problem -- no further action needed 494 } elsif ( defined( $main::envir{PROBLEM_GRADER_TO_USE} ) ) { 495 if (ref($main::envir{PROBLEM_GRADER_TO_USE}) eq 'CODE' ) { # user defined grader 496 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = $main::envir{PROBLEM_GRADER_TO_USE}; 497 } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'std_problem_grader' ) { 498 if (defined(&std_problem_grader) ){ 499 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl 500 } # std_problem_grader is the default in any case so don't give a warning. 501 } elsif ($main::envir{PROBLEM_GRADER_TO_USE} eq 'avg_problem_grader' ) { 502 if (defined(&avg_problem_grader) ){ 503 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&avg_problem_grader; # defined in PGanswermacros.pl 504 } 505 #else { # avg_problem_grader will be installed by PGtranslator so there is no need for a warning. 506 # warn "The problem grader 'avg_problem_grader' has not been defined. Has PGanswermacros.pl been loaded?"; 507 #} 508 } else { 509 warn "Error: $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} is not a known program grader."; 510 } 511 } elsif (defined(&std_problem_grader)) { 512 $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = \&std_problem_grader; # defined in PGanswermacros.pl 513 } else { 514 # PGtranslator will install its default problem grader 515 } 516 517 warn "ERROR: The problem grader is not a subroutine" unless ref( $main::PG_FLAGS{PROBLEM_GRADER_TO_USE}) eq 'CODE' 518 or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'std_problem_grader' 519 or $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} = 'avg_problem_grader'; 520 # return results 521 }; 522 523 $STRINGforOUTPUT .= '<SCRIPT> jsMath.wwProcess() </SCRIPT>' 524 if ($main::envir{displayMode} eq 'HTML_jsMath'); 525 526 if ($main::envir{displayMode} eq 'HTML_asciimath') { 527 $STRINGforOUTPUT .= '<SCRIPT> translate() </SCRIPT>'; 528 $STRINGforHEADER_TEXT .= 529 '<object id="mathplayer" classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987">' . "\n" . 530 '</object><?import namespace="mml" implementation="#mathplayer"?>' 531 unless ($STRINGforHEADER_TEXT =~ m/mathplayer/); 532 } 533 $STRINGforOUTPUT .= MODES(%{PG_restricted_eval('$main::problemPostamble')}); 534 535 (\$STRINGforOUTPUT, \$STRINGforHEADER_TEXT,\%PG_ANSWERS_HASH,eval(q!\%main::PG_FLAGS!)); 536 } 537 538 539 540 =head2 INITIALIZE_PG() 541 542 This is executed each C<DOCUMENT()> is called. For backward compatibility 543 C<loadMacros> also checks whether the C<macroDirectory> has been defined 544 and if not, it runs C<INITIALIZE_PG()> and issues a warning. 545 546 =cut 547 548 549 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |