-<?php // $Id$\r//\r///////////////////////////////////////////////////////////////\r// GIFT\r//\r// The GIFT import filter is an easy to use method for teachers \r// writing questions as a text file. It supports true-false, \r// short answer, multiple-choice and numerical questions, as well \r// as insertion of a blank line for the missing word format.\r//\r// Multiple Choice / Missing Word\r// Who's buried in Grant's tomb?{~Grant ~Jefferson =no one}\r// Grant is {~buried =entombed ~living} in Grant's tomb.\r// True-False:\r// Grant is buried in Grant's tomb.{FALSE}\r// Short-Answer.\r// Who's buried in Grant's tomb?{=no one =nobody}\r// Numerical\r// When was Ulysses S. Grant born?{#1922:5}\r//\r// Comment lines start with a double backslash (//). \r// Optional question names are enclosed in double colon(::). \r// Answer feedback is indicated with hash mark (#).\r// Percentage answer weights immediately follow the tilde (for\r// multiple choice) or equal sign (for short answer and numerical),\r// and are enclosed in percent signs (% %). Below are more\r// complicated examples with various options and formatting styles.\r// \r// ::Grant's Tomb::Grant is {\r// ~buried#No one is buried there.\r// =entombed#Right answer!\r// ~living#We hope not!\r// } in Grant's tomb.\r//\r// Difficult multiple choice question.{\r// ~wrong answer #comment on wrong answer\r// ~%50%half credit answer #comment on answer\r// =full credit answer #well done!}\r//\r// ::Jesus' hometown (Short answer ex.):: Jesus Christ was from {\r// =Nazareth#Yes! That's right!\r// =%75%Nazereth#Right, but misspelled.\r// =%25%Bethlehem#He was born here, but not raised here.\r// }.\r//\r// //this inline comment will be ignored by the filter\r// ::Numerical example::\r// When was Ulysses S. Grant born? {#\r// =1922:0 #Correct! 100% credit\r// =%50%1922:2 #He was born in 1922.\r// You get 50% credit for being close.\r// }\r// \r// This filter was written through the collaboration of numerous \r// members of the Moodle community. It was originally based on \r// the missingword format, which included code from Thomas Robb\r// and others. Paul Tsuchido Shew wrote this filter in December 2003.\r//////////////////////////////////////////////////////////////////////////\r// Based on default.php, included by ../import.php\r\rclass quiz_file_format extends quiz_default_format {\r\r function answerweightparser(&$answer) {\r $answer = substr($answer, 1); // removes initial %\r $end_position = strpos($answer, "%");\r $answer_weight = substr($answer, 0, $end_position); // gets weight as integer\r $answer_weight = $answer_weight/100; // converts to percent\r $answer = substr($answer, $end_position+1); // removes comment from answer\r return $answer_weight;\r }\r\r\r function commentparser(&$answer) {\r if (strpos($answer,"#") > 0){\r $hashpos = strpos($answer,"#");\r $comment = addslashes(substr($answer, $hashpos+1));\r $answer = substr($answer, 0, $hashpos);\r } else {\r $comment = " ";\r }\r return $comment;\r }\r \r\r function readquestion($lines) {\r // Given an array of lines known to define a question in this format, this function\r // converts it into a question object suitable for processing and insertion into Moodle.\r\r $question = NULL;\r $comment = NULL;\r define("GIFT_ANSWERWEIGHT_REGEX", "^%\-*([0-9]{1,2})\.?([0-9]*)%"); \r\r // REMOVED COMMENTED LINES and IMPLODE\r foreach ($lines as $key => $line) {\r $line = trim($line);\r if (substr($line, 0, 2) == "//") {\r // echo "Commented line removed.<br />";\r $lines[$key] = " ";\r }\r }\r\r $text = trim(implode(" ", $lines));\r\r if ($text == "") {\r // echo "<p>Empty line.</p>";\r return false;\r }\r\r // QUESTION NAME parser\r if (substr($text, 0, 2) == "::") {\r $text = substr($text, 2);\r\r $namefinish = strpos($text, "::");\r if ($namefinish === false) {\r $question->name = false;\r // name will be assigned after processing question text below\r } else {\r $question->name = addslashes(trim(substr($text, 0, $namefinish)));\r $text = trim(substr($text, $namefinish+2)); // Remove name from text\r }\r } else {\r $question->name = false;\r }\r\r\r // FIND ANSWER section\r $answerstart = strpos($text, "{");\r if ($answerstart === false) {\r if ($this->displayerrors) {\r echo "<P>$text<P>Could not find a {";\r }\r return false;\r }\r\r $answerfinish = strpos($text, "}");\r if ($answerfinish === false) {\r if ($this->displayerrors) {\r echo "<P>$text<P>Could not find a }";\r }\r return false;\r }\r\r $answerlength = $answerfinish - $answerstart;\r $answertext = trim(substr($text, $answerstart + 1, $answerlength - 1));\r\r // Format QUESTION TEXT without answer, inserting "_____" as necessary\r if (substr($text, -1) == "}") {\r // no blank line if answers follow question, outside of closing punctuation\r $question->questiontext = addslashes(trim(substr_replace($text, "", $answerstart, $answerlength+1)));\r } else {\r // inserts blank line for missing word format\r $question->questiontext = addslashes(trim(substr_replace($text, "_____", $answerstart, $answerlength+1)));\r }\r\r // set question name if not already set\r if ($question->name === false) {\r $question->name = $question->questiontext;\r }\r\r\r // determine QUESTION TYPE\r $question->qtype = NULL;\r\r if ($answertext{0} == "#"){\r $question->qtype = NUMERICAL;\r\r } elseif (strstr($answertext, "~") !== false) {\r // only Multiplechoice questions contain tilde ~\r $question->qtype = MULTICHOICE;\r \r } else { // either TRUEFALSE or SHORTANSWER\r \r // TRUEFALSE question check\r $truefalse_check = $answertext;\r if (strpos($answertext,"#") > 0){ \r // strip comments to check for TrueFalse question\r $truefalse_check = trim(substr($answertext, 0, strpos($answertext,"#")));\r }\r\r $valid_tf_answers = array("T", "TRUE", "F", "FALSE");\r if (in_array($truefalse_check, $valid_tf_answers)) {\r $question->qtype = TRUEFALSE;\r\r } else { // Must be SHORTANSWER\r $question->qtype = SHORTANSWER;\r }\r }\r\r if (!isset($question->qtype)) {\r if ($this->displayerrors) {\r echo "<P>$text<P>Question type not set.";\r }\r return false;\r }\r\r switch ($question->qtype) {\r case MULTICHOICE:\r if (strpos($answertext,"=") === false) {\r $question->single = 0; // multiple answers are enabled if no single answer is 100% correct \r } else {\r $question->single = 1; // only one answer allowed (the default)\r }\r\r $answertext = str_replace("=", "~=", $answertext);\r $answers = explode("~", $answertext);\r if (isset($answers[0])) {\r $answers[0] = trim($answers[0]);\r }\r if (empty($answers[0])) {\r array_shift($answers);\r }\r \r $countanswers = count($answers);\r if ($countanswers < 2) {\r if ($this->displayerrors) {\r echo "<P>$text<P>Found tilde for multiple choice, \r but too few answers for Multiple Choice.<br />\r Found <u>$countanswers</u> answers in answertext.";\r }\r return false;\r break;\r }\r \r foreach ($answers as $key => $answer) {\r $answer = trim($answer);\r\r // determine answer weight\r if ($answer[0] == "=") {\r $answer_weight = 1;\r $answer = substr($answer, 1);\r \r } elseif (ereg(GIFT_ANSWERWEIGHT_REGEX, $answer)) { // check for properly formatted answer weight\r $answer_weight = $this->answerweightparser($answer);\r \r } else { //default, i.e., wrong anwer\r $answer_weight = 0;\r }\r $question->fraction[$key] = $answer_weight;\r $question->feedback[$key] = $this->commentparser($answer); // commentparser also removes comment from $answer\r $question->answer[$key] = addslashes($answer); \r } // end foreach answer\r \r $question->defaultgrade = 1;\r $question->image = ""; // No images with this format\r return $question;\r break;\r \r case TRUEFALSE:\r $answer = $answertext;\r $comment = $this->commentparser($answer); // commentparser also removes comment from $answer\r\r if ($answer == "T" OR $answer == "TRUE") {\r $question->answer = 1;\r $question->feedbackfalse = $comment; //feedback if answer is wrong\r } else {\r $question->answer = 0;\r $question->feedbacktrue = $comment; //feedback if answer is wrong\r }\r $question->defaultgrade = 1;\r $question->image = ""; // No images with this format\r return $question;\r break;\r \r case SHORTANSWER:\r // SHORTANSWER Question\r $answers = explode("=", $answertext);\r if (isset($answers[0])) {\r $answers[0] = trim($answers[0]);\r }\r if (empty($answers[0])) {\r array_shift($answers);\r }\r \r if (count($answers) == 0) {\r // invalid question\r if ($this->displayerrors) {\r echo "<P>$text<P>Found equals=, but no answers in answertext";\r }\r return false;\r break;\r }\r\r foreach ($answers as $key => $answer) {\r $answer = trim($answer);\r\r // Answer Weight\r if (ereg(GIFT_ANSWERWEIGHT_REGEX, $answer)) { // check for properly formatted answer weight\r $answer_weight = $this->answerweightparser($answer);\r } else { //default, i.e., full-credit anwer\r $answer_weight = 1;\r }\r $question->fraction[$key] = $answer_weight;\r $question->feedback[$key] = $this->commentparser($answer); //commentparser also removes comment from $answer\r $question->answer[$key] = addslashes($answer);\r } // end foreach\r\r $question->usecase = 0; // Ignore case\r $question->defaultgrade = 1;\r $question->image = ""; // No images with this format\r return $question;\r break;\r\r case NUMERICAL:\r // Note similarities to ShortAnswer\r $answertext = substr($answertext, 1); // remove leading "#"\r\r $answers = explode("=", $answertext);\r if (isset($answers[0])) {\r $answers[0] = trim($answers[0]);\r }\r if (empty($answers[0])) {\r array_shift($answers);\r }\r \r if (count($answers) == 0) {\r // invalid question\r if ($this->displayerrors) {\r echo "<P>$text<P>No answers found in answertext (Numerical answer)";\r }\r return false;\r break;\r }\r\r foreach ($answers as $key => $answer) {\r $answer = trim($answer);\r\r // Answer weight\r if (ereg(GIFT_ANSWERWEIGHT_REGEX, $answer)) { // check for properly formatted answer weight\r $answer_weight = $this->answerweightparser($answer);\r } else { //default, i.e., full-credit anwer\r $answer_weight = 1;\r }\r $question->fraction[$key] = $answer_weight;\r $question->feedback[$key] = $this->commentparser($answer); //commentparser also removes comment from $answer\r\r //Calculate Answer and Min/Max values\r if (strpos($answer,"..") > 0) { // optional [min]..[max] format\r $marker = strpos($answer,"..");\r $question->max[$key] = trim(substr($answer, $marker+2));\r $question->min[$key] = trim(substr($answer, 0, $marker));\r $question->answer[$key] = ($question->max[$key] + $question->min[$key])/2;\r\r } elseif (strpos($answer,":") > 0){ // standard [answer]:[errormargin] format\r $marker = strpos($answer,":");\r $errormargin = trim(substr($answer, $marker+1));\r $question->answer[$key] = trim(substr($answer, 0, $marker));\r $question->max[$key] = $question->answer[$key] + $errormargin;\r $question->min[$key] = $question->answer[$key] - $errormargin;\r\r } else { // only one valid answer (zero errormargin)\r $errormargin = 0;\r $question->answer[$key] = trim($answer);\r $question->max[$key] = $question->answer[$key] + $errormargin;\r $question->min[$key] = $question->answer[$key] - $errormargin;\r }\r \r if (!is_numeric($question->answer[$key]) \r OR !is_numeric($question->max[$key])\r OR !is_numeric($question->max[$key])) {\r if ($this->displayerrors) {\r echo "<P>$text<P>For numerical questions, answer must be numbers.\r <P>Answer: <u>$answer</u><P>ErrorMargin: <u>$errormargin</u> .";\r }\r return false;\r break;\r }\r\r } // end foreach\r\r $question->defaultgrade = 1;\r $question->image = ""; // No images with this format\r return $question;\r break;\r\r default:\r if ($this->displayerrors) {\r echo "<P>$text<P> No valid question type. Error in switch(question->qtype)";\r }\r return false;\r break; \r \r } // end switch ($question->qtype)\r\r } // end function readquestion($lines)\r}\r\r?>\r
\ No newline at end of file
+<?php // $Id$\r//\r///////////////////////////////////////////////////////////////\r// GIFT\r//\r// The GIFT import filter is an easy to use method for teachers \r// writing questions as a text file. It supports true-false, \r// short answer, multiple-choice and numerical questions, as well \r// as insertion of a blank line for the missing word format.\r//\r// Multiple Choice / Missing Word\r// Who's buried in Grant's tomb?{~Grant ~Jefferson =no one}\r// Grant is {~buried =entombed ~living} in Grant's tomb.\r// True-False:\r// Grant is buried in Grant's tomb.{FALSE}\r// Short-Answer.\r// Who's buried in Grant's tomb?{=no one =nobody}\r// Numerical\r// When was Ulysses S. Grant born?{#1822:5}\r//\r// Comment lines start with a double backslash (//). \r// Optional question names are enclosed in double colon(::). \r// Answer feedback is indicated with hash mark (#).\r// Percentage answer weights immediately follow the tilde (for\r// multiple choice) or equal sign (for short answer and numerical),\r// and are enclosed in percent signs (% %). Below are more\r// complicated examples with various options and formatting styles.\r// \r// ::Grant's Tomb::Grant is {\r// ~buried#No one is buried there.\r// =entombed#Right answer!\r// ~living#We hope not!\r// } in Grant's tomb.\r//\r// Difficult multiple choice question.{\r// ~wrong answer #comment on wrong answer\r// ~%50%half credit answer #comment on answer\r// =full credit answer #well done!}\r//\r// ::Jesus' hometown (Short answer ex.):: Jesus Christ was from {\r// =Nazareth#Yes! That's right!\r// =%75%Nazereth#Right, but misspelled.\r// =%25%Bethlehem#He was born here, but not raised here.\r// }.\r//\r// //this inline comment will be ignored by the filter\r// ::Numerical example::\r// When was Ulysses S. Grant born? {#\r// =1822:0 #Correct! 100% credit\r// =%50%1982:2 #He was born in 1822.\r// You get 50% credit for being close.\r// }\r// \r// This filter was written through the collaboration of numerous \r// members of the Moodle community. It was originally based on \r// the missingword format, which included code from Thomas Robb\r// and others. Paul Tsuchido Shew wrote this filter in December 2003.\r//////////////////////////////////////////////////////////////////////////\r// Based on default.php, included by ../import.php\r\rclass quiz_file_format extends quiz_default_format {\r\r function answerweightparser(&$answer) {\r $answer = substr($answer, 1); // removes initial %\r $end_position = strpos($answer, "%");\r $answer_weight = substr($answer, 0, $end_position); // gets weight as integer\r $answer_weight = $answer_weight/100; // converts to percent\r $answer = substr($answer, $end_position+1); // removes comment from answer\r return $answer_weight;\r }\r\r\r function commentparser(&$answer) {\r if (strpos($answer,"#") > 0){\r $hashpos = strpos($answer,"#");\r $comment = addslashes(substr($answer, $hashpos+1));\r $answer = substr($answer, 0, $hashpos);\r } else {\r $comment = " ";\r }\r return $comment;\r }\r \r\r function readquestion($lines) {\r // Given an array of lines known to define a question in this format, this function\r // converts it into a question object suitable for processing and insertion into Moodle.\r\r $question = NULL;\r $comment = NULL;\r define("GIFT_ANSWERWEIGHT_REGEX", "^%\-*([0-9]{1,2})\.?([0-9]*)%"); \r\r // REMOVED COMMENTED LINES and IMPLODE\r foreach ($lines as $key => $line) {\r $line = trim($line);\r if (substr($line, 0, 2) == "//") {\r // echo "Commented line removed.<br />";\r $lines[$key] = " ";\r }\r }\r\r $text = trim(implode(" ", $lines));\r\r if ($text == "") {\r // echo "<p>Empty line.</p>";\r return false;\r }\r\r // QUESTION NAME parser\r if (substr($text, 0, 2) == "::") {\r $text = substr($text, 2);\r\r $namefinish = strpos($text, "::");\r if ($namefinish === false) {\r $question->name = false;\r // name will be assigned after processing question text below\r } else {\r $question->name = addslashes(trim(substr($text, 0, $namefinish)));\r $text = trim(substr($text, $namefinish+2)); // Remove name from text\r }\r } else {\r $question->name = false;\r }\r\r\r // FIND ANSWER section\r $answerstart = strpos($text, "{");\r if ($answerstart === false) {\r if ($this->displayerrors) {\r echo "<P>$text<P>Could not find a {";\r }\r return false;\r }\r\r $answerfinish = strpos($text, "}");\r if ($answerfinish === false) {\r if ($this->displayerrors) {\r echo "<P>$text<P>Could not find a }";\r }\r return false;\r }\r\r $answerlength = $answerfinish - $answerstart;\r $answertext = trim(substr($text, $answerstart + 1, $answerlength - 1));\r\r // Format QUESTION TEXT without answer, inserting "_____" as necessary\r if (substr($text, -1) == "}") {\r // no blank line if answers follow question, outside of closing punctuation\r $question->questiontext = addslashes(trim(substr_replace($text, "", $answerstart, $answerlength+1)));\r } else {\r // inserts blank line for missing word format\r $question->questiontext = addslashes(trim(substr_replace($text, "_____", $answerstart, $answerlength+1)));\r }\r\r // set question name if not already set\r if ($question->name === false) {\r $question->name = $question->questiontext;\r }\r\r\r // determine QUESTION TYPE\r $question->qtype = NULL;\r\r if ($answertext{0} == "#"){\r $question->qtype = NUMERICAL;\r\r } elseif (strstr($answertext, "~") !== false) {\r // only Multiplechoice questions contain tilde ~\r $question->qtype = MULTICHOICE;\r \r } else { // either TRUEFALSE or SHORTANSWER\r \r // TRUEFALSE question check\r $truefalse_check = $answertext;\r if (strpos($answertext,"#") > 0){ \r // strip comments to check for TrueFalse question\r $truefalse_check = trim(substr($answertext, 0, strpos($answertext,"#")));\r }\r\r $valid_tf_answers = array("T", "TRUE", "F", "FALSE");\r if (in_array($truefalse_check, $valid_tf_answers)) {\r $question->qtype = TRUEFALSE;\r\r } else { // Must be SHORTANSWER\r $question->qtype = SHORTANSWER;\r }\r }\r\r if (!isset($question->qtype)) {\r if ($this->displayerrors) {\r echo "<P>$text<P>Question type not set.";\r }\r return false;\r }\r\r switch ($question->qtype) {\r case MULTICHOICE:\r if (strpos($answertext,"=") === false) {\r $question->single = 0; // multiple answers are enabled if no single answer is 100% correct \r } else {\r $question->single = 1; // only one answer allowed (the default)\r }\r\r $answertext = str_replace("=", "~=", $answertext);\r $answers = explode("~", $answertext);\r if (isset($answers[0])) {\r $answers[0] = trim($answers[0]);\r }\r if (empty($answers[0])) {\r array_shift($answers);\r }\r \r $countanswers = count($answers);\r if ($countanswers < 2) {\r if ($this->displayerrors) {\r echo "<P>$text<P>Found tilde for multiple choice, \r but too few answers for Multiple Choice.<br />\r Found <u>$countanswers</u> answers in answertext.";\r }\r return false;\r break;\r }\r \r foreach ($answers as $key => $answer) {\r $answer = trim($answer);\r\r // determine answer weight\r if ($answer[0] == "=") {\r $answer_weight = 1;\r $answer = substr($answer, 1);\r \r } elseif (ereg(GIFT_ANSWERWEIGHT_REGEX, $answer)) { // check for properly formatted answer weight\r $answer_weight = $this->answerweightparser($answer);\r \r } else { //default, i.e., wrong anwer\r $answer_weight = 0;\r }\r $question->fraction[$key] = $answer_weight;\r $question->feedback[$key] = $this->commentparser($answer); // commentparser also removes comment from $answer\r $question->answer[$key] = addslashes($answer); \r } // end foreach answer\r \r $question->defaultgrade = 1;\r $question->image = ""; // No images with this format\r return $question;\r break;\r \r case TRUEFALSE:\r $answer = $answertext;\r $comment = $this->commentparser($answer); // commentparser also removes comment from $answer\r\r if ($answer == "T" OR $answer == "TRUE") {\r $question->answer = 1;\r $question->feedbackfalse = $comment; //feedback if answer is wrong\r } else {\r $question->answer = 0;\r $question->feedbacktrue = $comment; //feedback if answer is wrong\r }\r $question->defaultgrade = 1;\r $question->image = ""; // No images with this format\r return $question;\r break;\r \r case SHORTANSWER:\r // SHORTANSWER Question\r $answers = explode("=", $answertext);\r if (isset($answers[0])) {\r $answers[0] = trim($answers[0]);\r }\r if (empty($answers[0])) {\r array_shift($answers);\r }\r \r if (count($answers) == 0) {\r // invalid question\r if ($this->displayerrors) {\r echo "<P>$text<P>Found equals=, but no answers in answertext";\r }\r return false;\r break;\r }\r\r foreach ($answers as $key => $answer) {\r $answer = trim($answer);\r\r // Answer Weight\r if (ereg(GIFT_ANSWERWEIGHT_REGEX, $answer)) { // check for properly formatted answer weight\r $answer_weight = $this->answerweightparser($answer);\r } else { //default, i.e., full-credit anwer\r $answer_weight = 1;\r }\r $question->fraction[$key] = $answer_weight;\r $question->feedback[$key] = $this->commentparser($answer); //commentparser also removes comment from $answer\r $question->answer[$key] = addslashes($answer);\r } // end foreach\r\r $question->usecase = 0; // Ignore case\r $question->defaultgrade = 1;\r $question->image = ""; // No images with this format\r return $question;\r break;\r\r case NUMERICAL:\r // Note similarities to ShortAnswer\r $answertext = substr($answertext, 1); // remove leading "#"\r\r $answers = explode("=", $answertext);\r if (isset($answers[0])) {\r $answers[0] = trim($answers[0]);\r }\r if (empty($answers[0])) {\r array_shift($answers);\r }\r \r if (count($answers) == 0) {\r // invalid question\r if ($this->displayerrors) {\r echo "<P>$text<P>No answers found in answertext (Numerical answer)";\r }\r return false;\r break;\r }\r\r foreach ($answers as $key => $answer) {\r $answer = trim($answer);\r\r // Answer weight\r if (ereg(GIFT_ANSWERWEIGHT_REGEX, $answer)) { // check for properly formatted answer weight\r $answer_weight = $this->answerweightparser($answer);\r } else { //default, i.e., full-credit anwer\r $answer_weight = 1;\r }\r $question->fraction[$key] = $answer_weight;\r $question->feedback[$key] = $this->commentparser($answer); //commentparser also removes comment from $answer\r\r //Calculate Answer and Min/Max values\r if (strpos($answer,"..") > 0) { // optional [min]..[max] format\r $marker = strpos($answer,"..");\r $question->max[$key] = trim(substr($answer, $marker+2));\r $question->min[$key] = trim(substr($answer, 0, $marker));\r $question->answer[$key] = ($question->max[$key] + $question->min[$key])/2;\r\r } elseif (strpos($answer,":") > 0){ // standard [answer]:[errormargin] format\r $marker = strpos($answer,":");\r $errormargin = trim(substr($answer, $marker+1));\r $question->answer[$key] = trim(substr($answer, 0, $marker));\r $question->max[$key] = $question->answer[$key] + $errormargin;\r $question->min[$key] = $question->answer[$key] - $errormargin;\r\r } else { // only one valid answer (zero errormargin)\r $errormargin = 0;\r $question->answer[$key] = trim($answer);\r $question->max[$key] = $question->answer[$key] + $errormargin;\r $question->min[$key] = $question->answer[$key] - $errormargin;\r }\r \r if (!is_numeric($question->answer[$key]) \r OR !is_numeric($question->max[$key])\r OR !is_numeric($question->max[$key])) {\r if ($this->displayerrors) {\r echo "<P>$text<P>For numerical questions, answer must be numbers.\r <P>Answer: <u>$answer</u><P>ErrorMargin: <u>$errormargin</u> .";\r }\r return false;\r break;\r }\r\r } // end foreach\r\r $question->defaultgrade = 1;\r $question->image = ""; // No images with this format\r return $question;\r break;\r\r default:\r if ($this->displayerrors) {\r echo "<P>$text<P> No valid question type. Error in switch(question->qtype)";\r }\r return false;\r break; \r \r } // end switch ($question->qtype)\r\r } // end function readquestion($lines)\r}\r\r?>\r
\ No newline at end of file