void TextAreaOverlayElement::initialise(void) { if (!mInitialised) { // Set up the render op // Combine positions and texture coords since they tend to change together // since character sizes are different mRenderOp.vertexData = OGRE_NEW VertexData(); VertexDeclaration* decl = mRenderOp.vertexData->vertexDeclaration; size_t offset = 0; // Positions decl->addElement(POS_TEX_BINDING, offset, VET_FLOAT3, VES_POSITION); offset += VertexElement::getTypeSize(VET_FLOAT3); // Texcoords decl->addElement(POS_TEX_BINDING, offset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); // Colours - store these in a separate buffer because they change less often decl->addElement(COLOUR_BINDING, 0, VET_COLOUR, VES_DIFFUSE); mRenderOp.operationType = RenderOperation::OT_TRIANGLE_LIST; mRenderOp.useIndexes = false; mRenderOp.vertexData->vertexStart = 0; mRenderOp.useGlobalInstancingVertexBufferIsAvailable = false; // Vertex buffer will be created in checkMemoryAllocation mRenderOp.srcRenderable = this; checkMemoryAllocation( DEFAULT_INITIAL_CHARS ); mInitialised = true; } }
// process configuration file int readSearchSpecFile(struct scalpelState *state) { int lineNumber = 0, status; FILE *f; char *buffer = (char *) malloc( (NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH + 1) * sizeof(char)); checkMemoryAllocation(state, buffer, __LINE__, __FILE__, "buffer"); f = fopen(state->conffile, "r"); if (f == NULL ) { fprintf(stderr, "ERROR: Couldn't open configuration file:\n%s -- %s\n", state->conffile, strerror(errno)); free(buffer); buffer = NULL; return SCALPEL_ERROR_FATAL_READ; } while (fgets(buffer, NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH, f)) { lineNumber++; if (state->specLines > MAX_FILE_TYPES) { fprintf(stderr, "Your conf file contains too many file types.\n"); fprintf(stderr, "This version was compiled with MAX_FILE_TYPES == %d.\n", MAX_FILE_TYPES); fprintf(stderr,"Increase MAX_FILE_TYPES, recompile, and try again.\n"); free(buffer); buffer = NULL; return SCALPEL_ERROR_TOO_MANY_TYPES; } if ((status = processSearchSpecLine(state, buffer, lineNumber)) != SCALPEL_OK) { free(buffer); buffer = NULL; return status; } } // add an empty object to the end of the list as a marker state->SearchSpec[state->specLines].suffix = NULL; state->SearchSpec[state->specLines].casesensitive = 0; state->SearchSpec[state->specLines].length = 0; state->SearchSpec[state->specLines].begin = NULL; state->SearchSpec[state->specLines].beginlength = 0; state->SearchSpec[state->specLines].end = NULL; state->SearchSpec[state->specLines].endlength = 0; // GGRIII: offsets field is uninitialized--it doesn't // matter, since we won't use this entry. fclose(f); free(buffer); buffer = NULL; return SCALPEL_OK; }
void TextAreaOverlayElement::updatePositionGeometry() { float *pVert; if (mFont.isNull()) { // not initialised yet, probably due to the order of creation in a template return; } size_t charlen = mCaption.size(); checkMemoryAllocation( charlen ); mRenderOp.vertexData->vertexCount = charlen * 6; // Get position / texcoord buffer const HardwareVertexBufferSharedPtr& vbuf = mRenderOp.vertexData->vertexBufferBinding->getBuffer(POS_TEX_BINDING); pVert = static_cast<float*>( vbuf->lock(HardwareBuffer::HBL_DISCARD, Root::getSingleton().getFreqUpdatedBuffersUploadOption()) ); float largestWidth = 0; float left = _getDerivedLeft() * 2.0f - 1.0f; float top = -( (_getDerivedTop() * 2.0f ) - 1.0f ); // Derive space with from a number 0 if(!mSpaceWidthOverridden) { mSpaceWidth = mFont->getGlyphAspectRatio(UNICODE_ZERO) * mCharHeight; } // Use iterator DisplayString::iterator i, iend; iend = mCaption.end(); bool newLine = true; for( i = mCaption.begin(); i != iend; ++i ) { if( newLine ) { Real len = 0.0f; for( DisplayString::iterator j = i; j != iend; j++ ) { Font::CodePoint character = OGRE_DEREF_DISPLAYSTRING_ITERATOR(j); if (character == UNICODE_CR || character == UNICODE_NEL || character == UNICODE_LF) { break; } else if (character == UNICODE_SPACE) // space { len += mSpaceWidth * 2.0f * mViewportAspectCoef; } else { len += mFont->getGlyphAspectRatio(character) * mCharHeight * 2.0f * mViewportAspectCoef; } } if( mAlignment == Right ) left -= len; else if( mAlignment == Center ) left -= len * 0.5f; newLine = false; } Font::CodePoint character = OGRE_DEREF_DISPLAYSTRING_ITERATOR(i); if (character == UNICODE_CR || character == UNICODE_NEL || character == UNICODE_LF) { left = _getDerivedLeft() * 2.0f - 1.0f; top -= mCharHeight * 2.0f; newLine = true; // Also reduce tri count mRenderOp.vertexData->vertexCount -= 6; // consume CR/LF in one if (character == UNICODE_CR) { DisplayString::iterator peeki = i; peeki++; if (peeki != iend && OGRE_DEREF_DISPLAYSTRING_ITERATOR(peeki) == UNICODE_LF) { i = peeki; // skip both as one newline // Also reduce tri count mRenderOp.vertexData->vertexCount -= 6; } } continue; } else if (character == UNICODE_SPACE) // space { // Just leave a gap, no tris left += mSpaceWidth * 2.0f * mViewportAspectCoef; // Also reduce tri count mRenderOp.vertexData->vertexCount -= 6; continue; } Real horiz_height = mFont->getGlyphAspectRatio(character) * mViewportAspectCoef ; const Font::UVRect& uvRect = mFont->getGlyphTexCoords(character); // each vert is (x, y, z, u, v) //------------------------------------------------------------------------------------- // First tri // // Upper left *pVert++ = left; *pVert++ = top; *pVert++ = -1.0; *pVert++ = uvRect.left; *pVert++ = uvRect.top; top -= mCharHeight * 2.0f; // Bottom left *pVert++ = left; *pVert++ = top; *pVert++ = -1.0; *pVert++ = uvRect.left; *pVert++ = uvRect.bottom; top += mCharHeight * 2.0f; left += horiz_height * mCharHeight * 2.0f; // Top right *pVert++ = left; *pVert++ = top; *pVert++ = -1.0; *pVert++ = uvRect.right; *pVert++ = uvRect.top; //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // Second tri // // Top right (again) *pVert++ = left; *pVert++ = top; *pVert++ = -1.0; *pVert++ = uvRect.right; *pVert++ = uvRect.top; top -= mCharHeight * 2.0f; left -= horiz_height * mCharHeight * 2.0f; // Bottom left (again) *pVert++ = left; *pVert++ = top; *pVert++ = -1.0; *pVert++ = uvRect.left; *pVert++ = uvRect.bottom; left += horiz_height * mCharHeight * 2.0f; // Bottom right *pVert++ = left; *pVert++ = top; *pVert++ = -1.0; *pVert++ = uvRect.right; *pVert++ = uvRect.bottom; //------------------------------------------------------------------------------------- // Go back up with top top += mCharHeight * 2.0f; float currentWidth = (left + 1)/2 - _getDerivedLeft(); if (currentWidth > largestWidth) { largestWidth = currentWidth; } } // Unlock vertex buffer vbuf->unlock(); if (mMetricsMode == GMM_PIXELS) { // Derive parametric version of dimensions Real vpWidth; vpWidth = (Real) (OverlayManager::getSingleton().getViewportWidth()); largestWidth *= vpWidth; }; if (getWidth() < largestWidth) setWidth(largestWidth); }
// Returns TRUE if the directory exists and is empty. // If the directory does not exist, an attempt is made to // create it. On error, returns FALSE int outputDirectoryOK(char *dir) { DIR *temp; struct dirent *entry; int i; mode_t newDirectoryMode; if((temp = opendir(dir)) == NULL) { // If the directory doesn't exist (ENOENT), we will create it if(errno == ENOENT) { // The directory mode values come from the chmod(2) man page #ifdef __MINGW32__ newDirectoryMode = 0; if(mkdir(dir)) { #else newDirectoryMode = (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH); if(mkdir(dir, newDirectoryMode)) { #endif fprintf(stderr, "An error occured while trying to create %s - %s\n", dir, strerror(errno)); return FALSE; } // try to open directory if((temp = opendir(dir)) == NULL) { fprintf(stderr, "An error occured while trying to open %s - %s\n", dir, strerror(errno)); return FALSE; } } else { fprintf(stderr, "An error occured while trying to open %s - %s\n", dir, strerror(errno)); return FALSE; } } // verify directory is empty--there should be only two entries, // "." and ".." i = 0; while ((entry = readdir(temp))) { if(i > 1) { return FALSE; } i++; } closedir(temp); return TRUE; } // open audit file and add initial entries int openAuditFile(struct scalpelState *state) { time_t now = time(NULL); char *timestring = ctime(&now); char fn[MAX_STRING_LENGTH]; char *buf; int err = SCALPEL_OK; buf = (char *)malloc(NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH); checkMemoryAllocation(state, buf, __LINE__, __FILE__, "buf"); if(!outputDirectoryOK(state->outputdirectory)) { err = SCALPEL_ERROR_NONEMPTY_DIRECTORY; goto out; } snprintf(fn, MAX_STRING_LENGTH, "%s/audit.txt", state->outputdirectory); FILE *f; if(!(state->auditFile = fopen(fn, "w"))) { fprintf(stderr, "Couldn't open audit file\n%s -- %s\n", fn, strerror(errno)); err = SCALPEL_ERROR_FATAL_READ; goto out; } fprintf(state->auditFile, "\nScalpel version %s audit file\n" "Started at %sCommand line:\n%s\n\n" "Output directory: %s\n" "Configuration file: %s\n", SCALPEL_VERSION, timestring, state->invocation, state->outputdirectory, state->conffile); // copy config file into audit log f = fopen(state->conffile, "r"); if(f == NULL) { fprintf(stderr, "ERROR: Couldn't open configuration file:\n%s -- %s\n", state->conffile, strerror(errno)); err = SCALPEL_ERROR_FATAL_READ; goto out; } fprintf(state->auditFile, "\n------ BEGIN COPY OF CONFIG FILE USED ------\n"); while (fgets(buf, NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH, f)) { fprintf(state->auditFile, "%s", buf); } fprintf(state->auditFile, "------ END COPY OF CONFIG FILE USED ------\n\n"); fclose(f); out: free(buf); return err; }
// Return the remaining size, in bytes, of an open file stream. On // error, return -1. Handling raw device files is substantially more // complicated than image files. For Linux, an ioctl() is used. For // other operating systems, a "binary search" technique similar to // that in e2fsprogs getsize.c is used. long long measureOpenFile(FILE * f, struct scalpelState *state) { unsigned long long total = 0, original = ftello(f); int descriptor = 0; struct stat *info; unsigned long long numsectors = 0; if((fseeko(f, 0, SEEK_END))) { if(state->modeVerbose) { fprintf(stdout, "fseeko() call failed on image file.\n"); fprintf(stdout, "Diagnosis: %s\n", strerror(errno)); } return -1; } total = ftello(f); // for block devices (e.g., raw disk devices), calculating size by // seeking the end of the opened stream doesn't work. For Linux, we use // an ioctl() call. For others (e.g., OS X), we use binary search. // is it a block device? descriptor = fileno(f); info = (struct stat *)malloc(sizeof(struct stat)); checkMemoryAllocation(state, info, __LINE__, __FILE__, "info"); fstat(descriptor, info); if(S_ISBLK(info->st_mode)) { #if defined (__linux) if(ioctl(descriptor, BLKGETSIZE, &numsectors) < 0) { if(state->modeVerbose) { fprintf(stdout, "Using ioctl() call to measure block device size.\n"); } #if defined(__DEBUG) perror("BLKGETSIZE failed"); #endif } #else // non-Linux, use binary search { unsigned long long low, high, mid; fprintf(stdout, "Using binary search to measure block device size.\n"); low = 0; for(high = 512; valid_offset(descriptor, high); high *= 2) { low = high; } while (low < high - 1) { mid = (low + high) / 2; if(valid_offset(descriptor, mid)) { low = mid; } else { high = mid; } } numsectors = (low + 1) >> 9; } #endif // assume device has 512 byte sectors total = numsectors * 512; free(info); } // restore file position if((fseeko(f, original, SEEK_SET))) { if(state->modeVerbose) { fprintf(stdout, "fseeko() call to restore file position failed on image file.\n"); } return -1; } return (total - original); }
int extractSearchSpecData(struct scalpelState *state, struct SearchSpecLine *s, char **tokenarray) { int err = 0; // process one line from config file: // token[0] = suffix // token[1] = case sensitive? // token[2] = maximum carve size // token[3] = begintag // token[4] = endtag // token[5] = search type (optional) s->suffix = (char *) malloc(MAX_SUFFIX_LENGTH * sizeof(char)); checkMemoryAllocation(state, s->suffix, __LINE__, __FILE__, "s->suffix"); s->begin = (char *) malloc(MAX_STRING_LENGTH * sizeof(char)); checkMemoryAllocation(state, s->begin, __LINE__, __FILE__, "s->begin"); s->end = (char *) malloc(MAX_STRING_LENGTH * sizeof(char)); checkMemoryAllocation(state, s->end, __LINE__, __FILE__, "s->end"); s->begintext = (char *) malloc(MAX_STRING_LENGTH * sizeof(char)); checkMemoryAllocation(state, s->begintext, __LINE__, __FILE__, "s->begintext"); s->endtext = (char *) malloc(MAX_STRING_LENGTH * sizeof(char)); checkMemoryAllocation(state, s->endtext, __LINE__, __FILE__, "s->endtext"); if (!strncasecmp(tokenarray[0], SCALPEL_NOEXTENSION_SUFFIX, strlen(SCALPEL_NOEXTENSION_SUFFIX))) { s->suffix[0] = SCALPEL_NOEXTENSION; s->suffix[1] = 0; } else { memcpy(s->suffix, tokenarray[0], MAX_SUFFIX_LENGTH); } // case sensitivity check s->casesensitive = (!strncasecmp(tokenarray[1], "y", 1) || !strncasecmp(tokenarray[1], "yes", 3)); //#ifdef _WIN32 // s->length = _atoi64(tokenarray[2]); //#else // s->length = atoull(tokenarray[2]); //#endif char split[MAX_STRING_LENGTH]; char *maxcarvelength; strcpy(split, tokenarray[2]); maxcarvelength = strchr(split, ':'); if (!maxcarvelength) { s->minlength = 0; s->length = strtoull(split, 0, 10); } else { *maxcarvelength = 0; maxcarvelength++; s->minlength = strtoull(split, 0, 10); s->length = strtoull(maxcarvelength, 0, 10); } // determine search type for this needle s->searchtype = SEARCHTYPE_FORWARD; if (!strncasecmp(tokenarray[5], "REVERSE", strlen("REVERSE"))) { s->searchtype = SEARCHTYPE_REVERSE; } else if (!strncasecmp(tokenarray[5], "NEXT", strlen("NEXT"))) { s->searchtype = SEARCHTYPE_FORWARD_NEXT; } // FORWARD is the default, but OK if the user defines it explicitly else if (!strncasecmp(tokenarray[5], "FORWARD", strlen("FORWARD"))) { s->searchtype = SEARCHTYPE_FORWARD; } // regular expressions must be handled separately if (isRegularExpression(tokenarray[3])) { #ifdef GPU_THREADING // GPU execution does not support regex needles. std::stringstream ss; ss << "ERROR: GPU search for regex headers is not supported!\n"; ss << "Please modify the config file for non-regex headers only.\n"; std::string msg = ss.str(); fprintf(stderr, msg.c_str()); throw std::runtime_error(msg); #endif // copy RE, zap leading/training '/' and prepare for regular expression compilation s->beginisRE = 1; strcpy(s->begin, tokenarray[3]); strcpy(s->begintext, tokenarray[3]); s->beginlength = strlen(tokenarray[3]); s->begin[s->beginlength] = 0; // compile regular expression err = regncomp(&(s->beginstate.re), s->begin + 1, s->beginlength - 2, REG_EXTENDED | (REG_ICASE * (!s->casesensitive))); if (err) { return SCALPEL_ERROR_BAD_HEADER_REGEX; } } else { // non-regular expression header s->beginisRE = 0; strcpy(s->begintext, tokenarray[3]); s->beginlength = translate(tokenarray[3]); memcpy(s->begin, tokenarray[3], s->beginlength); init_bm_table(s->begin, s->beginstate.bm_table, s->beginlength, s->casesensitive); } if (isRegularExpression(tokenarray[4])) { #ifdef GPU_THREADING // GPU execution does not support regex needles. std::stringstream ss; ss << "ERROR: GPU search for regex footers is not supported!\n"; ss << "Please modify the config file for non-regex footers only.\n"; std::string msg = ss.str(); fprintf(stderr, msg.c_str()); throw std::runtime_error(msg); #endif // copy RE, zap leading/training '/' and prepare for for regular expression compilation s->endisRE = 1; strcpy(s->end, tokenarray[4]); strcpy(s->endtext, tokenarray[4]); s->endlength = strlen(tokenarray[4]); s->end[s->endlength] = 0; // compile regular expression err = regncomp(&(s->endstate.re), s->end + 1, s->endlength - 2, REG_EXTENDED | (REG_ICASE * (!s->casesensitive))); if (err) { return SCALPEL_ERROR_BAD_FOOTER_REGEX; } } else { s->endisRE = 0; strcpy(s->endtext, tokenarray[4]); s->endlength = translate(tokenarray[4]); memcpy(s->end, tokenarray[4], s->endlength); init_bm_table(s->end, s->endstate.bm_table, s->endlength, s->casesensitive); } return SCALPEL_OK; }
// initialize state variable and copy command line arguments if passed in (argv can be NULL) void initializeState(char ** argv, struct scalpelState *state) { char **argvcopy = argv; int sss; int i; state->inReader = NULL; // Allocate memory for state state->inputFileList = (char *) malloc(MAX_STRING_LENGTH * sizeof(char)); checkMemoryAllocation(state, state->inputFileList, __LINE__, __FILE__, "state->inputFileList"); state->conffile = (char *) malloc(MAX_STRING_LENGTH * sizeof(char)); checkMemoryAllocation(state, state->conffile, __LINE__, __FILE__, "state->conffile"); state->outputdirectory = (char *) malloc(MAX_STRING_LENGTH * sizeof(char)); checkMemoryAllocation(state, state->conffile, __LINE__, __FILE__, "state->outputdirectory"); state->invocation = (char *) malloc(MAX_STRING_LENGTH * sizeof(char)); checkMemoryAllocation(state, state->invocation, __LINE__, __FILE__, "state->invocation"); // GGRIII: memory allocation made more sane, because we're storing // more information in Scalpel than foremost had to, for each file // type. sss = (MAX_FILE_TYPES + 1) * sizeof(struct SearchSpecLine); state->SearchSpec = (struct SearchSpecLine *) malloc(sss); memset(state->SearchSpec, 0, sss); checkMemoryAllocation(state, state->SearchSpec, __LINE__, __FILE__, "state->SearchSpec"); state->specLines = 0; // GGRIII: initialize header/footer offset data, carved file count, // et al. The header/footer database is re-initialized in "dig.c" // after each image file is processed (numfilestocarve and // organizeDirNum are not). Storage for the header/footer offsets // will be reallocated as needed. for (i = 0; i < MAX_FILE_TYPES; i++) { state->SearchSpec[i].offsets.headers = 0; state->SearchSpec[i].offsets.headerlens = 0; state->SearchSpec[i].offsets.footers = 0; state->SearchSpec[i].offsets.footerlens = 0; state->SearchSpec[i].offsets.numheaders = 0; state->SearchSpec[i].offsets.numfooters = 0; state->SearchSpec[i].offsets.headerstorage = 0; state->SearchSpec[i].offsets.footerstorage = 0; state->SearchSpec[i].numfilestocarve = 0; state->SearchSpec[i].organizeDirNum = 0; } state->fileswritten = 0; state->skip = 0; state->organizeMaxFilesPerSub = MAX_FILES_PER_SUBDIRECTORY; state->modeVerbose = FALSE; state->modeNoSuffix = FALSE; state->useInputFileList = FALSE; state->carveWithMissingFooters = FALSE; state->noSearchOverlap = FALSE; state->generateHeaderFooterDatabase = FALSE; state->updateCoverageBlockmap = FALSE; state->useCoverageBlockmap = FALSE; state->coverageblocksize = 0; state->blockAlignedOnly = FALSE; state->organizeSubdirectories = TRUE; state->previewMode = FALSE; state->handleEmbedded = FALSE; state->auditFile = NULL; inputReaderVerbose = FALSE; // default values for output directory, config file, wildcard character, // coverage blockmap directory strncpy(state->outputdirectory, SCALPEL_DEFAULT_OUTPUT_DIR, strlen(SCALPEL_DEFAULT_OUTPUT_DIR)); strncpy(state->conffile, SCALPEL_DEFAULT_CONFIG_FILE, MAX_STRING_LENGTH); state->coveragefile = state->outputdirectory; wildcard = SCALPEL_DEFAULT_WILDCARD; signal_caught = 0; state->invocation[0] = 0; // copy the invocation string into the state do { strncat(state->invocation, *argvcopy, MAX_STRING_LENGTH - strlen(state->invocation)); strncat(state->invocation, " ", MAX_STRING_LENGTH - strlen(state->invocation)); ++argvcopy; } while (*argvcopy); }
int processSearchSpecLine(struct scalpelState *state, char *buffer, int lineNumber) { char *buf = buffer; char *token; int i = 0, err = 0, len = strlen(buffer); // murder CTRL-M (0x0d) characters // if(buffer[len - 2] == 0x0d && buffer[len - 1] == 0x0a) { if (len >= 2 && buffer[len - 2] == 0x0d && buffer[len - 1] == 0x0a) { buffer[len - 2] = buffer[len - 1]; buffer[len - 1] = buffer[len]; } buf = (char *) skipWhiteSpace(buf); token = strtok(buf, " \t\n"); // lines beginning with # are comments if (token == NULL || token[0] == '#') { return SCALPEL_OK; } // allow wildcard to be changed if (!strncasecmp(token, "wildcard", 9)) { if ((token = strtok(NULL, " \t\n")) != NULL ) { translate(token); } else { fprintf(stdout, "Warning: Empty wildcard in configuration file line %d. Ignoring.\n", lineNumber); return SCALPEL_OK; } if(strlen(token) > 1) { fprintf(stderr, "Warning: Wildcard can only be one character," " but you specified %d characters.\n" " Using the first character, \"%c\", as the wildcard.\n", (int)strlen(token), token[0]); } wildcard = token[0]; return SCALPEL_OK; } char **tokenarray = (char **) malloc( 6 * sizeof(char[MAX_STRING_LENGTH + 1])); checkMemoryAllocation(state, tokenarray, __LINE__, __FILE__, "tokenarray"); while (token && (i < NUM_SEARCH_SPEC_ELEMENTS)) { tokenarray[i] = token; i++; token = strtok(NULL, " \t\n"); } switch (NUM_SEARCH_SPEC_ELEMENTS - i) { case 2: tokenarray[NUM_SEARCH_SPEC_ELEMENTS - 1] = (char *) ""; tokenarray[NUM_SEARCH_SPEC_ELEMENTS - 2] = (char *) ""; break; case 1: tokenarray[NUM_SEARCH_SPEC_ELEMENTS - 1] = (char *) ""; break; case 0: break; default: fprintf(stderr, "\nERROR: In line %d of the configuration file, expected %d tokens,\n" " but instead found only %d.\n", lineNumber, NUM_SEARCH_SPEC_ELEMENTS, i); free(tokenarray); return SCALPEL_ERROR_NO_SEARCH_SPEC; break; } if ((err = extractSearchSpecData(state, &(state->SearchSpec[state->specLines]), tokenarray))) { switch (err) { case SCALPEL_ERROR_BAD_HEADER_REGEX: fprintf(stderr, "\nERROR: In line %d of the configuration file, bad regular expression for header.\n", lineNumber) ; break; case SCALPEL_ERROR_BAD_FOOTER_REGEX: fprintf(stderr, "\nERROR: In line %d of the configuration file, bad regular expression for footer.\n", lineNumber); break; default: fprintf(stderr, "\nERROR: Unknown error on line %d of the configuration file.\n", lineNumber); } } state->specLines++; free(tokenarray); return SCALPEL_OK; }