function pyBoxHandler($options, $content) { //echo "PB[[[".$content."]]]"; // given a shortcode, print out the html for the user, // and save the relevant grader options in a hash file. $id = generateId(); if ($options == FALSE) { $options = array(); } // wordpress does a weird thing where valueless for ($i = 0; array_key_exists($i, $options); $i++) { // attributes map like [0]=>'attname'. $options[$options[$i]] = "Y"; // these lines change it to unset($options[$i]); // 'attname'=>'Y' } $shortcodeOptions = json_encode($options); // this will be put into DB. /// do some cleaning-up and preprocessing of options, and create the problem info for grader if (array_key_exists('translate', $options)) { $GLOBALS['pb_translation'] = $options['translate']; } if (array_key_exists('pyexample', $options)) { setSoft($options, 'grader', '*nograder*'); setSoft($options, 'readonly', 'Y'); setSoft($options, 'hideemptyinput', 'Y'); unset($options['pyexample']); } if (array_key_exists('code', $options)) { // sugar: code is an alias for defaultcode $options["defaultcode"] = $options["code"]; unset($options['code']); } $richreadonly = array_key_exists('richreadonly', $options); // sugar if ($richreadonly) { $options['readonly'] = "Y"; unset($options['richreadonly']); } if (array_key_exists('nograder', $options)) { // syntactic sugar for nograder option if (array_key_exists('grader', $options)) { pyboxlog('Warning: grader overwritten with *nograder*'); } $options["grader"] = "*nograder*"; unset($options['nograder']); } foreach ($options as $optname => $optvalue) { // syntactic sugar for inplace grader if (preg_match('|tests|', $optname) > 0 || preg_match('|precode|', $optname) > 0) { $options["inplace"] = "Y"; } } global $post, $lesson_reg_info; // $lessonNumber is numeric (major) part of lesson number // if lesson_reg_info is set we don't really care about displaying the pybox, // but we'll do things for consistency anyway. NB: when displaying a problem // in a place other than its original page (e.g., mail) this needs ot be fixed $post_title = isset($lesson_reg_info) ? get_the_title($lesson_reg_info['id']) : $post->post_name; if (preg_match('|^(\\d+).*|', $post_title, $matches) == 0) { $lessonNumber = -1; } else { $lessonNumber = $matches[1]; } $inplace = booleanize(getSoft($options, 'inplace', 'N')); $scramble = booleanize(getSoft($options, 'scramble', 'N')); // important booleans used to determine $readonly = booleanize(getSoft($options, 'readonly', 'N')); // other options... get them first $showEditorToggle = booleanize(getSoft($options, 'showeditortoggle', 'N')); unset($options['scramble']); unset($options['readonly']); // don't extract() these! unset($options['inplace']); if ($inplace) { setSoft($options, 'hideemptyoutput', 'Y'); } $defaultValues = array('defaultcode' => FALSE, 'autocommentline' => $lessonNumber > 3 && !($scramble || $readonly), 'console' => 'N', 'rows' => 10, 'allowinput' => $lessonNumber > 5 && !$scramble && !$readonly, 'disablericheditor' => ($lessonNumber > -1 && $lessonNumber < 7 || $scramble || $readonly) && !$richreadonly, 'usertni' => $inplace); foreach ($defaultValues as $key => $value) { if (!array_key_exists($key, $options)) { $options[$key] = $defaultValues[$key]; } } extract($options); $allowinput = booleanize($allowinput); $disablericheditor = booleanize($disablericheditor); $console = booleanize($console); $autocommentline = booleanize($autocommentline); $usertni = booleanize($usertni); $facultative = isset($grader) && $grader == '*nograder*' || $console || $readonly; if ($scramble || $readonly) { $options['nolog'] = 'Y'; } // for grader. note that if they are absent, their default values are 'N' if ($facultative) { $options['facultative'] = 'Y'; } else { unset($options['facultative']); } if ($allowinput) { $options['allowinput'] = 'Y'; } else { unset($options['allowinput']); } if ($scramble) { $options['scramble'] = 'Y'; } // already unset if ($readonly) { $options['readonly'] = 'Y'; } if ($inplace) { $options['inplace'] = 'Y'; } if ($usertni) { $options['usertni'] = 'Y'; } else { unset($options['usertni']); } $cosmeticOptions = array('defaultcode', 'autocommentline', 'console', 'rows', 'disablericheditor', 'scramble', 'readonly', 'showeditortoggle', 'title', 'placeholder'); $copyForGrader = array(); foreach ($options as $optname => $optvalue) { if (!in_array($optname, $cosmeticOptions)) { $copyForGrader[$optname] = $optvalue; } } if (array_key_exists('maxeditdistance', $options)) { $copyForGrader['originalcode'] = $defaultcode; } $optionsJson = json_encode($copyForGrader); $hash = md5($shortcodeOptions . $optionsJson); $slug = getSoft($options, 'slug', 'NULL'); registerPybox($id, $slug, $scramble ? "scramble" : "code", $facultative, getSoft($options, 'title', NULL), $content, $shortcodeOptions, $hash, $optionsJson); if (isMakingDatabases()) { $res = do_short_and_sweetcode($content); // faster db generation with accurate count $GLOBALS['pb_translation'] = NULL; return $res; } /// we've delivered options to the grader. get on with producing html if ($defaultcode === FALSE && $scramble && $solver !== FALSE) { $lines = explode("\n", trim(softSafeDereference($solver))); shuffle($lines); $defaultcode = implode("\n", $lines); } if ($defaultcode !== FALSE) { try { $defaultcode = softSafeDereference($defaultcode); } catch (PyboxException $e) { $GLOBALS['pb_translation'] = NULL; return pberror("PyBox error: defaultcode file " . $defaultcode . " not found."); } $defaultcode = ensureNewlineTerminated($defaultcode); } /// actually start outputting here. part 1: headers and description $r = ''; $readyScripts = ''; $r .= '<form class="pbform" action="#" id="pbform' . $id . '" method="POST">' . "\n"; if ($scramble) { $c = "scramble"; } else { if (debugEnabled()) { $c = "debug"; } else { $c = ""; } } if ($facultative) { $c .= " facultative"; } $r .= "<div class='pybox modeNeutral {$c}' id='pybox{$id}'>\n"; if (!$facultative && !array_key_exists("slug", $options)) { pyboxlog("Hash " . $hash . " not read-only, but needs a slug", TRUE); $r .= slugwarn(); } if ($facultative) { if ($console) { unset($options["title"]); $r .= heading("Console", $options); } else { $r .= heading(__t('Example'), $options); } } else { $r .= checkbox($slug); $r .= heading($scramble ? __t('Scramble Exercise') : __t('Coding Exercise'), $options); //if ($scramble) // $r .= "<b>Note (Dec 13)</b>: scramble exercises are temporarily broken — sorry!<br>"; } $r .= do_short_and_sweetcode($content); //instructions, problem description. process any shortcodes inside. // part 1.5: help box if (!$facultative && !$scramble) { $r .= '<div class="helpOuter" style="display: none;"><div class="helpInner">'; if (!is_user_logged_in()) { $r .= '<div style="text-align: center">' . __t('You need to create an account and log in to ask a question.') . '</div>'; } else { global $wpdb; $guru_login = get_the_author_meta('pbguru', get_current_user_id()); if ($guru_login != '') { $guruid = $wpdb->get_var($wpdb->prepare('SELECT ID from ' . $wpdb->prefix . 'users WHERE user_login = %s', $guru_login)); } $r .= '<div style="text-align: center">'; if ($guru_login != '' and $guruid !== NULL) { $r .= __t('Send a question by e-mail to: '); $r .= "<select class='recipient'>\n<option value='1'>" . __t("My guru") . " ({$guru_login})</option>\n<option value='-1'>" . __t("CS Circles Assistant") . "</option>\n</select></div>"; } else { $r .= __t('Send a question by e-mail to: '); $r .= "<select class='recipient'>\n<option value='-1'>" . __t("CS Circles Assistant") . "</option>\n<option value='0'>" . __t("(No guru specified in your profile)") . "</option>\n</select>"; $r .= '<br/></div>'; } $r .= __t("Enter text for the message below. <i>Be sure to explain where you're stuck and what you've tried so far. Your partial solution code will be automatically included with the message.</i>"); $r .= "<textarea style='font-family: serif'></textarea>"; $r .= "<table class='helpControls'><tr class='wp-core-ui'><td style='width: 50%'><a class='button' onclick='sendMessage({$id},\"{$slug}\")'>" . __t("Send this message") . "</a></td><td style='width: 50%'>\n <a class='button' onclick='helpClick({$id})'>" . __t("Cancel") . "</a></td></tr></table>"; } $r .= '</div></div>'; } /// part 2: code input if ($readonly) { $thecode = trim($defaultcode); $rows = count(explode("\n", $thecode)); } elseif ($console == "Y" && array_key_exists("consolecode", $_GET)) { $thecode = htmlspecialchars(html_entity_decode(stripslashes($_GET["consolecode"]))); $rows = count(explode("\n", $thecode)) + 1; } else { $thecode = $defaultcode; if ($autocommentline) { $thecode .= __t('# delete this comment and enter your code here') . "\n"; } if (array_key_exists('slug', $options) && $scramble === FALSE) { $savedCode = loadMostRecent($options['slug']); if ($savedCode !== NULL) { $thecode = $savedCode; } } } if ($scramble) { $r .= '<ul class="pyscramble" name="pyscramble" id="pyscramble' . $id . '">' . "\n"; foreach (explode("\n", rtrim($thecode)) as $s) { if (strpos($s, 'delete this comment') === FALSE) { // fix an old bug -- got stuck in database $r .= ' <li class="pyscramble">' . (trim($s) == '' ? ' ' : htmlspecialchars(html_entity_decode($s))) . "</li>\n"; } } $r .= "</ul>\n"; $r .= "<input type='hidden' id='usercode{$id}' name='usercode{$id}'/>\n"; } else { // $r .= "<div class='acecontain ace_hide' id='acecontain$id' ><div class='aceinner' id='ace$id'></div></div>"; $px = $rows * 26 + 6; //+6 for border, padding in weird box model $h = $px; if (!$readonly) { $h = max(50, $h); } $h = " style='height: {$h}px;'"; $ro = $readonly ? "readonly='readonly'" : ""; $c = $readonly ? "RO" : "RW"; $p = $readonly ? " style = 'height : {$px}px;' " : ""; $s = $readonly ? "" : "resizy"; //cols=... required for valid html but width actually set by css //height is set explicitly since it's the only way for IE to render the textarea at the correct height $pl = array_key_exists("placeholder", $options) ? "placeholder='" . $options['placeholder'] . "'" : ""; $r .= "<div class='pyboxTextwrap pyboxCodewrap {$c} {$s}' {$h}><textarea wrap='off' name='usercode{$id}' id='usercode{$id}' {$pl} cols=10 rows={$rows} {$ro} {$p} class='pyboxCode {$c}'>\n"; $r .= $thecode; $r .= '</textarea></div>' . "\n"; } // part 2.5 history container $r .= "<div id='pbhistory{$id}' class='flexcontain' style='display:none;'></div>\n"; /// part 3: stdin if ($allowinput) { if ($usertni === TRUE) { $description = __t('Enter testing statements like <tt>print(myfunction("test argument"))</tt> below.'); } else { $description = __t("You may enter input for the program in the box below."); } $r .= '<div name="pyinput" id="pyinput' . $id . '">'; $r .= $description; $r .= '<div class="pyboxTextwrap resizy" style="height: 102px;" ><textarea wrap="off" name="userinput" class="pyboxInput" cols=10 rows=4></textarea></div>'; $r .= '</div>' . "\n"; //cols=10 required for valid html but width actually set by css } /// part 4: controls $tni = $usertni ? 'Y' : 'N'; $actions = array(); if ($allowinput) { $actions['switch'] = array('id' => "switch{$id}", 'value' => 'Input Switch', 'onclick' => "pbInputSwitch({$id},'{$tni}')"); } if (!$disablericheditor) { // $userLikesRich = (!is_user_logged_in()) || ("true"!==get_the_author_meta( 'pbplain', get_current_user_id())); $userLikesRich = TRUE; if ($showEditorToggle || $richreadonly) { $actions['CMtoggle'] = array('value' => __t('Rich editor'), 'id' => "toggleCM{$id}", 'onclick' => "pbToggleCodeMirror({$id})"); } if ($userLikesRich) { $readyScripts .= "jQuery(function(){pbToggleCodeMirror({$id});});"; } } if (!$scramble && !$console && ($lessonNumber >= 4 || $lessonNumber < 0)) { $actions['consolecopy'] = array('value' => __t('Open in console'), 'onclick' => "pbConsoleCopy({$id})"); } if (!$scramble && ($lessonNumber >= 4 || $lessonNumber < 0)) { $actions['visualize'] = array('value' => __t('Visualize'), 'onclick' => "pbVisualize({$id},'{$tni}')"); } if (!$readonly && !$scramble) { //$actions['save'] = array('value'=>'Save without running', 'onclick'=>"pbSave($id)"); if (array_key_exists("slug", $options)) { $historyAction = "historyClick({$id},'{$slug}')"; $actions['history'] = array('value' => __t('History'), 'onclick' => $historyAction); } if (!($readonly === "Y") && $defaultcode != '' && $defaultcode !== FALSE) { // prepare the string for javaScript enclosure // we put in single-quotes, rather than json's default double quotes, for compatibility with // our $button usage $dc = substr(json_encode(htmlspecialchars_decode($defaultcode, ENT_QUOTES), JSON_HEX_APOS), 1, -1); $r .= "<input type='hidden' id='defaultCode{$id}' value='{$dc}'></input>\n"; $actions['default'] = array('value' => __t('Reset code to default'), 'onclick' => "pbSetText({$id},descape(\$('#defaultCode{$id}').val()))"); } } if (!$facultative && !$scramble && !get_option('cscircles_hide_help')) { $actions['help'] = array('value' => __t('Help'), 'onclick' => "helpClick({$id});"); } if ($richreadonly) { $actions = array('CMtoggle' => $actions['CMtoggle']); } // get rid of all other options $r .= "<div class='pyboxbuttons'><table><tr>\n"; if (!$richreadonly) { $r .= "<td><input type='submit' name='submit' id='submit{$id}' value=' '/></td>\n"; } $mb = 3; //maximum number of buttons, not counting 'submit' $i = 0; foreach ($actions as $name => $atts) { $i++; if ($i <= $mb) { $r .= button($name, $atts); continue; } if ($i == 1 + $mb) { $r .= "</tr></table><select id='pbSelect{$id}' class='selectmore'><option name='more'>" . __t("More actions...") . "</option>\n"; } $r .= option($name, $atts); } if (count($actions) > $mb) { $r .= "</select></div>\n"; } else { $r .= "</tr></table></div>\n"; } if (isset($cpulimit) && $cpulimit != 1) { $timeout = (WALLFACTOR * $cpulimit + WALLBUFFER + 2) * 1000; // + 2 seconds for network latency each way $r .= "<input type='hidden' name='timeout' value='{$timeout}'/>\n"; } $r .= '<input type="hidden" name="lang" value="' . currLang4() . '"/>'; $r .= '<input type="hidden" id="inputInUse' . $id . '" name="inputInUse" value="Y"/>' . "\n"; $r .= '<input type="hidden" name="pyId" value="' . $id . '"/>' . "\n"; $r .= '<input type="hidden" name="hash" value="' . $hash . '"/>' . "\n"; // although inputInUse starts as Y, the next script sets it to N and fixes the button labels $readyScripts .= $allowinput ? 'pbInputSwitch(' . $id . ',"' . ($usertni ? 'Y' : 'N') . '");' : 'document.getElementById("submit' . $id . '").value = "' . __t('Run program') . '";' . 'document.getElementById("inputInUse' . $id . '").value = "N";'; /// part 5 : results area, and footers $c = count($actions) > $mb ? ' avoidline' : ''; $r .= "<div id='pbresults{$id}' class='pbresults{$c}'></div>\n"; $r .= problemSourceWidget(array('hash' => $hash), count($actions) > $mb); $r .= '</div>' . "\n"; $r .= '</form>' . "\n"; if ($readyScripts != '') { $r .= "<script type='text/javascript'>{$readyScripts}</script>\n"; } $GLOBALS['pb_translation'] = NULL; return $r; }
function doGrading($usercode, $TC) { $files = array(); if ($TC['showonly'] !== FALSE) { $desired = explode(" ", $TC['showonly']); foreach ($TC as $name => $value) { if (substr($name, 0, 4) == "show") { $TC[$name] = in_array(substr($name, 4), $desired) ? "Y" : "N"; } } } if ($TC['answer'] !== FALSE) { $TC['answer'] = ensureNewlineTerminated($TC['answer']); } $TC["inplace"] = booleanize($TC["inplace"]); extract($TC); // same as $showinput = $TC["showinput"], etc $mainFile = ""; $er = FALSE; $mainFile .= "from _UTILITIES import *\n"; $lang = 'en_US'; if (array_key_exists("lang", $_REQUEST) && preg_match("~^[a-zA-Z_]*\$~", $_REQUEST["lang"])) { $lang = $_REQUEST["lang"]; } $mainFile .= "_setLanguage('{$lang}')\n"; $inputMaker = inputMaker($TC); $noInput = $inputMaker === FALSE; $mainFile .= ($inputMaker === FALSE ? "_stdin=''" : $inputMaker) . "\n_stdincopy = open('stdincopy', 'w', encoding='utf-8')\nprint(_stdin, file=_stdincopy, end='')\n_stdincopy.close()\n"; if ($precode !== FALSE) { $mainFile .= softSafeDereference($precode) . "\n"; } $files['stdincopy'] = NULL; $mainFile .= "import _GRADER\n"; $mainFile .= "_G = _GRADER\n"; global $inputInUse, $facultative; if (!$inputInUse && ($inplace || $solver !== FALSE)) { if ($solver !== FALSE) { $files['solver'] = $solver; } if ($solver !== FALSE) { $mainFile .= "_GRADER.globalsInitAndEcho(globals())\n"; } else { $mainFile .= "_GRADER.globalsInitAndEcho(globals(), False)\n"; } $files['graderreply'] = NULL; $files['graderpre'] = NULL; $files['solverstdout'] = NULL; // run the solver before usercode, lest they mess up our globals. $mainFile .= "_GRADER.runSolverWithTests()\n"; $testcode = ""; if ($rawtests !== FALSE) { $testcode .= $rawtests . "\n"; } if ($autotests != FALSE) { $autotests = softSafeDereference($autotests); $python_ident_regex = '(\\p{L}|\\p{Nl}|_)(\\p{L}|\\p{N}|\\p{Mn}|\\p{Mc}|\\p{Pc})*'; // note: this is close to, but not technically, 100% the // same as the formal Python definition of an identifier $py_regex_parens = 2; foreach (explode("\n", $autotests) as $autotestline) { if (preg_match('|^(\\s*)(\\S.*)$|', $autotestline, $matches) === 0) { continue; } //skip blank lines $indentation = $matches[1]; $command = trim($matches[2]); if (1 == preg_match('@^' . $python_ident_regex . '$@u', $command)) { //varname $testcode .= $indentation . "_G.checkVar('{$command}')\n"; } elseif (1 == preg_match('@^(' . $python_ident_regex . ')\\s*\\((.*)\\)$@u', $command, $pieces)) { if (strpos($pieces[2], $pieces[1]) === FALSE) { // looks like a non-self-nested function call $testcode .= $indentation . "_G.autotestCall('" . $pieces[1] . "',[" . $pieces[2 + $py_regex_parens] . "])\n"; } else { // something more complex $testcode .= $indentation . "_G.sayRunning(\"" . $command . "\")\n"; $testcode .= $indentation . "_G.autotestCompare(\"" . $command . "\", {$command})\n"; } } else { $testcode .= $autotestline . "\n"; } // just leave it alone } } $files['testcode'] = $testcode === FALSE ? "" : softSafeDereference($testcode) . "\n"; } $mainFile .= ' _orig_std = (_sys.stdin, _sys.stdout) _user_stdout = _StringIO() _sys.stdout = _TeeOut(_user_stdout, _orig_std[1]) _sys.stdin = _StringIO(_stdin) exec(compile(open(\'usercode\', encoding="utf-8").read(), ' . '\'usercode\', \'exec\')) '; if (!$inputInUse) { // lesson 18, part 2: may do this even if facultative if ($inplace) { $mainFile .= "exec(compile(open('testcode', encoding='utf-8').read()," . " 'testcode', 'exec'))\n"; $mainFile .= "_G.say('Y', 'noend')\n"; // success if none of the tests crash } } // we've got all the user stdout necessary for testing $mainFile .= ' __user_stdout = _user_stdout.getvalue() _user_stdout.close() (_sys.stdin, _sys.stdout) = _orig_std '; if (!$facultative && !$inputInUse) { if ($answer !== FALSE) { $mainFile .= "_G._solver_stdout = " . pythonEscape(softSafeDereference($answer)) . "\n"; } if ($grader !== '*nograder*' && ($answer !== FALSE || $solver !== FALSE)) { $mainFile .= "_G.stdoutGrading(_stdin,__user_stdout,_G._solver_stdout, " . pythonEscape(softSafeDereference($grader)) . " )\n"; $files['stdoutgraderreply'] = NULL; } } $testDescription = FALSE; $files["usercode"] = $usercode; global $usertni; if ($inputInUse && $usertni) { $mainFile .= "\n" . "exec(compile(open('usertests', encoding='utf-8').read(), " . "'usertests', 'exec'))\n"; global $userinput; $files['usertests'] = $userinput; } $files["mainfile"] = $mainFile; $userResult = safepython($files, "mainfile", "", $cpulimit); extract($userResult); // start printing stuff out now. $m = ''; if ($testDescription != FALSE) { $m .= $testDescription; } if (!$inputInUse && $inplace && trim($outdata['graderpre']) != '') { $m .= '<i>' . __t('Before running your code:') . '</i> ' . $outdata['graderpre'] . '<br/>'; } if ($showinput == "Y" && !$inputInUse && !$noInput && ($hideemptyinput == "N" || $outdata['stdincopy'] != "")) { $m .= __t("Input:") . preBox($outdata['stdincopy']); } global $submit_code_stderr, $submit_code_errnice; $submit_code_stderr = $stderr; $submit_code_errnice = stderrNiceify($stderr); if (userIsAdmin() && $stderrlen > 0) { $m .= JQpopUp("Debug: view unsanitized", preBox($stderr, $stderrlen)); } if ($stderr == '') { $errnice = ''; } else { $errnice = '<p>' . __t('Error messages: ') . preBoxHinted(stderrNiceify($stderr), $stderrlen) . '</p>'; } if ($ok) { $m .= "<p>" . __t('Program executed without crashing.') . "</p>"; } elseif (firstLine($safeexecOut) == 'Command exited with non-zero status (1)') { $m .= "<p>" . __t("Program crashed.") . "</p>"; } else { $m .= "<p>" . __t("Program crashed — ") . firstLine($safeexecOut) . ".</p>"; } if (1 === 2) { // these lines are just to trick gettext __t("Memory Limit Exceeded") . __t("Time Limit Exceeded") . __t("Command exited with non-zero status") . __t("Command terminated by signal") . __t("Output Limit Exceeded") . __t("Invalid Function") . __t("Internal Error"); } if ($showsafeexec == "Y") { $m .= "Sandbox messages:" . preBox($safeexecOut); } $simpleOutputDescription = outputDescription(NULL, array('showoutput' => $showoutput, 'stdoutlen' => $stdoutlen, 'hideemptyoutput' => $hideemptyoutput, 'stdout' => $stdout, 'ok' => $ok)); if ($desirederror !== FALSE) { $m .= $simpleOutputDescription; $lines = explode("\n", trim($userResult["stderr"])); $goodFail = count($lines) > 0 && $lines[count($lines) - 1] == $desirederror; $m .= $errnice; return $goodFail ? tcpass($m) : tcfail($m); } if (!$ok || $facultative) { // we don't care what's in stdout $graderreply = trim(getSoft($outdata, 'graderreply', '')); if (!$ok && !$inputInUse && $graderreply != '') { $m .= "<i>" . __t("The grader said:") . "</i>" . "<div>" . $graderreply . "</div>"; } elseif ($inplace && $solver === FALSE & $graderreply != '') { $m .= "<i>" . __t("Automatic tests:") . "</i>" . "<div>" . substr($graderreply, 0, -1) . "</div>"; } $m .= $errnice . $simpleOutputDescription; return $ok ? tcpass($m) : tcfail($m); } if ($inplace) { // don't care what's in stdout, unless solverstdout != '' $GR = $outdata['graderreply']; $inplaceresult = substr($GR, -1); $inplacereply = substr($GR, 0, -1); if ($inplacereply != '') { $inplacereply = "<i>" . __t("The grader said:") . "</i>" . "<div>{$inplacereply}</div>"; } if ($inplaceresult == 'Y') { if ($outdata['solverstdout'] == '') { return tcpass($m . $inplacereply . $errnice . $simpleOutputDescription); } } elseif ($inplaceresult == 'N') { return tcfail($m . $inplacereply . $errnice . $simpleOutputDescription); } $m .= $inplacereply; // carry on and let the stdout grader do its thing } // the user's code did not crash. what did the stdout grader say? $outGraderReply = $outdata['stdoutgraderreply']; if ($outGraderReply == "" || !($outGraderReply[0] == "Y" || $outGraderReply[0] == "N")) { throw new PyboxException("Grader error 2 [" . $outGraderReply . '|' . $outdata['graderreply'] . "|" . ord(substr($outdata['graderreply'], -1)) . "| {$m} ]"); } $outinfo = array('stdout' => $stdout, 'stdoutlen' => $stdoutlen, 'requiredStdout' => getSoft($outdata, 'solverstdout', $answer), 'showoutput' => $showoutput, 'showexpected' => $showexpected, 'grader' => $grader); $m .= outputDescription($outGraderReply[0] == "Y", $outinfo) . $errnice; if (strlen(trim($outGraderReply)) > 1) { $m .= "<p>" . __t("Result of grading: ") . substr($outGraderReply, 1) . "</p>"; } return $outGraderReply[0] == "Y" ? tcpass($m) : tcfail($m); }