std::string runAndCapture(int (* mainFunc)(), const std::string& cinInput, const std::string& outputFileName) { // run the 'main' function, possibly feeding it cin, input, and capture its cout output if (!cinInput.empty()) { ioutils::redirectStdinBegin(cinInput); } ioutils::setConsoleOutputLimit(MAX_STUDENT_OUTPUT); // prevent infinite output by student ioutils::captureStdoutBegin(); mainFunc(); std::string output = ioutils::captureStdoutEnd(); if (!cinInput.empty()) { ioutils::redirectStdinEnd(); } // return the output as a string (and also possibly write it to a file) if (!outputFileName.empty()) { writeEntireFile(outputFileName, output); } return output; }
bool SearchContext::searchFile(int id, const std::string &filename, RegexList &filespecRegexes, pcre *matchRegex) { bool matchesOneFilespec = false; for(RegexList::iterator it = filespecRegexes.begin(); it != filespecRegexes.end(); ++it) { pcre *regex = *it; if(pcre_exec(regex, NULL, filename.c_str(), filename.length(), 0, 0, NULL, 0) >= 0) { matchesOneFilespec = true; break; } } if(!matchesOneFilespec) return false; std::string contents; if(!readEntireFile(filename, contents, params_.maxFileSize)) return false; std::string workBuffer = contents; std::string updatedContents; bool atLeastOneMatch = false; int lineNumber = 1; char *p = &workBuffer[0]; char *line; while((line = nextToken(&p, '\n')) != NULL) { char *originalLine = line; std::string replacedLine; SearchEntry entry; int ovector[100]; do { bool matches = false; int matchPos; int matchLen; if(matchRegex) { int rc; if(rc = pcre_exec(matchRegex, 0, line, strlen(line), 0, 0, ovector, sizeof(ovector)) >= 0) { matches = true; matchPos = ovector[0]; matchLen = ovector[1] - ovector[0]; } } else { char *match; if(params_.flags & SF_MATCH_CASE_SENSITIVE) match = strstr(line, params_.match.c_str()); else match = strstri(line, params_.match.c_str()); if(match != NULL) { matches = true; matchPos = match - line; matchLen = params_.match.length(); } } if(params_.flags & SF_REPLACE) { if(matches) { replacedLine.append(line, matchPos); entry.highlights_.push_back(Highlight(replacedLine.length(), params_.replace.length())); replacedLine.append(params_.replace); line += matchPos + matchLen; } else { break; } } else { if(matches) { entry.filename_ = filename; entry.match_ = originalLine; entry.line_ = lineNumber; entry.highlights_.push_back(Highlight(matchPos + (line - originalLine), matchLen)); line += matchPos + matchLen; } else { break; } } if(matches) hits_++; } while(*line); if(!entry.filename_.empty()) { linesWithHits_++; atLeastOneMatch = true; } if(params_.flags & SF_REPLACE) { if(line && *line) replacedLine += line; if(replacedLine != originalLine) { entry.filename_ = filename; entry.match_ = replacedLine; entry.line_ = lineNumber; append(id, entry); } replacedLine += "\n"; updatedContents += replacedLine; } else { if(!entry.filename_.empty()) append(id, entry); } lineNumber++; } if(atLeastOneMatch) filesWithHits_++; if(params_.flags & SF_REPLACE) { if((contents != updatedContents)) { bool overwriteFile = true; if(params_.flags & SF_BACKUP) { std::string backupFilename = filename; backupFilename += "."; backupFilename += params_.backupExtension; if(!writeEntireFile(backupFilename, contents)) { std::string err = "WARNING: Couldn't write backup file (skipping replacement): "; err += backupFilename; err += "\n"; poke(id, err.c_str(), HighlightList(), 0, false); overwriteFile = false; } } if(overwriteFile) { if(writeEntireFile(filename, updatedContents)) { return true; } else { std::string err = "WARNING: Couldn't write to file: "; err += filename; err += "\n"; poke(id, err.c_str(), HighlightList(), 0, false); } } } return false; } return true; }
bool SearchContext::searchFile(int id, const std::string &filename, RegexList &filespecRegexes, pcre *matchRegex) { bool matchesOneFilespec = false; for(RegexList::iterator it = filespecRegexes.begin(); it != filespecRegexes.end(); ++it) { pcre *regex = *it; if(pcre_exec(regex, NULL, filename.c_str(), filename.length(), 0, 0, NULL, 0) >= 0) { matchesOneFilespec = true; break; } } if(!matchesOneFilespec) return false; std::string contents; if(!readEntireFile(filename, contents, params_.maxFileSize)) return false; std::string workBuffer = contents; std::string updatedContents; std::deque<char *> contextLines; bool atLeastOneMatch = false; int trailingContextLines = 0; int lineNumber = 1; char *p = &workBuffer[0]; char *line; while((line = nextToken(&p, '\n')) != NULL) { char *originalLine = line; std::string replacedLine; SearchEntry entry; int ovector[100]; // Strip newline, but remember exactly what kind it was bool hasCarriageReturn = false; int lineLen = strlen(line); if(lineLen && (line[lineLen - 1] == '\r')) { line[lineLen - 1] = 0; hasCarriageReturn = true; } // Matching loop (we might find our string a few times on a single line) bool lineMatched = false; do { bool matches = false; int matchPos; int matchLen; // The actual match. Either invoke PCRE or do a boring strstr if(matchRegex) { int rc; if(rc = pcre_exec(matchRegex, 0, line, strlen(line), 0, 0, ovector, sizeof(ovector)) >= 0) { matches = true; matchPos = ovector[0]; matchLen = ovector[1] - ovector[0]; } } else { char *match; if(params_.flags & SF_MATCH_CASE_SENSITIVE) match = strstr(line, params_.match.c_str()); else match = strstri(line, params_.match.c_str()); if(match != NULL) { matches = true; matchPos = match - line; matchLen = params_.match.length(); } } if(matches) lineMatched = true; // Handle the match. For replace or find, we: // * Add output explaining the match // * Advance the line pointer for another match attempt // ... or ... // * "break", which leaves the matching loop if(params_.flags & SF_REPLACE) { if(matches) { std::string temp(line, matchPos); replacedLine.append(temp); replacedLine.append(params_.replace); entry.textBlocks.addBlock(temp, config_.textColor_); entry.textBlocks.addBlock(params_.replace, config_.highlightColor_, true); line += matchPos + matchLen; } else { break; } } else { if(matches) { entry.textBlocks.addHighlightedBlock(originalLine, matchPos + (line - originalLine), matchLen, config_.textColor_, config_.highlightColor_, true); line += matchPos + matchLen; } else { break; } } if(matches) hits_++; } while(*line); // end of matching loop // If we're doing a replace, finish the line and append to the final updated contents if(params_.flags & SF_REPLACE) { if(line && *line) { replacedLine += line; entry.textBlocks.addBlock(line, config_.textColor_); } if(hasCarriageReturn) { replacedLine += "\r"; } replacedLine += "\n"; updatedContents += replacedLine; } bool outputMatch = false; if(lineMatched) { // keep stats linesWithHits_++; atLeastOneMatch = true; // If we matched, consider notifying the user. We'll always say something // unless the replaced text doesn't actually change the line. outputMatch = ( !(params_.flags & SF_REPLACE) ) || (replacedLine != originalLine); if(outputMatch) { entry.filename_ = filename; entry.line_ = lineNumber; // output all existing context lines if(contextLines.size()) { SearchEntry contextEntry; contextEntry.filename_ = entry.filename_; contextEntry.contextOnly_ = true; int currLine = entry.line_ - contextLines.size(); for(std::deque<char *>::iterator it = contextLines.begin(); it != contextLines.end(); ++it) { TextBlockList textBlocks; textBlocks.addBlock(*it, config_.textColor_); contextEntry.textBlocks.swap(textBlocks); contextEntry.line_ = currLine++; append(id, contextEntry); } contextLines.clear(); } append(id, entry); } // Remember that we'd like the next few lines, even if they don't match trailingContextLines = config_.contextLines_; } if(!outputMatch) { // Didn't output a match. Keep track or output the line anyway for contextual reasons. if(trailingContextLines > 0) { // A recent match wants to see this line in the output anyway SearchEntry trailingEntry; trailingEntry.filename_ = filename; trailingEntry.line_ = lineNumber; trailingEntry.contextOnly_ = true; trailingEntry.textBlocks.addBlock(originalLine, config_.textColor_); append(id, trailingEntry); trailingContextLines--; } else { // didn't output a match, and wasn't output as context. stash it in contextLines contextLines.push_back(originalLine); if((int)contextLines.size() > config_.contextLines_) contextLines.pop_front(); } } lineNumber++; } // end of line loop (done reading file) if(atLeastOneMatch) filesWithHits_++; if(params_.flags & SF_REPLACE) { if((contents != updatedContents)) { bool overwriteFile = true; if(params_.flags & SF_BACKUP) { std::string backupFilename = filename; backupFilename += "."; backupFilename += params_.backupExtension; if(!writeEntireFile(backupFilename, contents)) { std::string err = "WARNING: Couldn't write backup file (skipping replacement): "; err += backupFilename; err += "\n"; sendError(id, err); overwriteFile = false; } } if(overwriteFile) { if(writeEntireFile(filename, updatedContents)) { return true; } else { std::string err = "WARNING: Couldn't write to file: "; err += filename; err += "\n"; sendError(id, err); } } } return false; } return true; }