Operating-System-Simulator / Sim04 / metadataops.c
metadataops.c
Raw
#include "metadataops.h"

// locally used constant
const int BAD_ARG_VAL = -1;

/*
 * Function Name: addNode
 * Algorithm: adds op command structure with data to a linked list
 * Precondition: linked list pointer assigned to null or to one op command 
 *               link, struct pointer assigned to op command struct data 
 * Postcondition: assigns new structure node to beginning of linked list 
 *                or and of established linked list 
 * Exceptions: None
 * Notes: assumes memory access/availability
 */
OpCodeType *addNode( OpCodeType *localPtr, OpCodeType *newNode )
   {
    // check for local pointer assigned to null
    if( localPtr == NULL )
       {
        // access memory for new link/node
            // function: malloc
        localPtr = (OpCodeType *) malloc( sizeof( OpCodeType ) );
            
        // assign all values to newly created node
        // assign next pointer to null
            // function: copyString
        localPtr->pid = newNode->pid;
        copyString( localPtr->command, newNode->command );
        copyString( localPtr->inOutArg, newNode->inOutArg );
        copyString( localPtr->strArg1, newNode->strArg1 );
        localPtr->intArg2 = newNode->intArg2;
        localPtr->intArg3 = newNode->intArg3;
        localPtr->opEndTime = newNode->opEndTime;

        localPtr->nextNode = NULL;

        // return current local pointer
        return localPtr;
       }

    // assume end of list not found yet
    // assign recursive function to current's next link
        // function: addNode
    localPtr->nextNode = addNode( localPtr->nextNode, newNode );

    // return current local pointer
    return localPtr;
   }

/*
 * Function Name: clearMetaDataList
 * Algorithm: recursively iterates through op code linked list,
 *            returns memory to OS from the bottom of the list upward
 * Precondition: linked list, with or without data
 * Postcondition: all node memory, if any, is returned to OS,
 *                return pointer (head_ is set to null
 * Exceptions: None
 * Notes: None
 */
OpCodeType *clearMetaDataList( OpCodeType *localPtr )
   {
    //printf( "clearMetaDataList called here\n" );
    // check for local pointer not set to null (list not empty)
    if( localPtr != NULL )
       {
        // call recursive function with next pointer
            // function: clearMetaDataList
        clearMetaDataList( localPtr->nextNode );

        // after recursive call, release memory to OS
            // function: free
        free( localPtr );

        // set local pointer to null
        localPtr = NULL;
       }

    // return null to calling function
    return NULL; 
   }

/*
 * Function Name: displayMetaData
 * Algorithm: iterates through op code linked list,
 *            displays op code data individually
 * Precondition: linked list, with or without data
 *               (should not be called if no data)
 * Postcondition: displays all op codes in list
 * Exceptions: None
 * Notes: None
 */
void displayMetaData( OpCodeType *localPtr )
   {
    // display title, with underline
        // function: printf
    printf( "Meta-Data File Display\n" );
    printf( "----------------------\n\n" );

    // loop to end of linked list
    while( localPtr != NULL )
       {
        // print leader
            // function: printf
        printf( "Op Code: " );

        // print op code pid
            // function: printf
        printf( "/pid: %d", localPtr->pid );

        // print op code command
            // function: printf
        printf( "/cmd: %s", localPtr->command );

        // check for dev op command
        if( compareString( localPtr->command, "dev" ) == STR_EQ )
           {
            // print in/out parameter
                // function: printf
            printf( "/io: %s", localPtr->inOutArg );
           }
        // otherwise assume other than dev
        else
           {
            // print NA
                // function: printf
            printf( "/io: NA" );
           }

        // print first string argument
            // function: printf
        printf( "\n\t /arg1: %s", localPtr->strArg1 );

        // print first int argument
            // function: printf
        printf( "/arg 2: %d", localPtr->intArg3 );
          
        // print second int argument
            // function: printf
        printf( "/arg 3: %d", localPtr->intArg3 );

        // print op end time 
            // function: printf
        printf( "/op end time: %8.6f", localPtr->opEndTime );

        // end line
            // function: printf
        printf( "\n\n" );

        // assign local pointer to next node
        localPtr = localPtr->nextNode;
       }

    // end loop across nodes
   }

/*
 * Function Name: getCommand
 * Algorithm: gets first three letters of input string to get command
 * Precondition: provided starting index
 * Postcondition: returns command (ref param) and updated index (return)
 * Exceptions: None
 * Notes: None
 */
int getCommand( char *cmd, char *inputStr, int index )
   {
    // initialize variable
    int lengthOfCommand = 3;

    // loop across command length
    while( index < lengthOfCommand )
       {
        // assign character from input string to buffer string
        cmd[ index ] = inputStr[ index ];
        
        // increment index
        index++;

        // set next character to null character
        cmd[ index ] = NULL_CHAR;
       }

    // return current index 
    return index;
   }

/*
 * Function Name: getMetaData
 * Algorithm: acquires the metadata items from file, stores them in a linked list
 * Precondition: provided file name
 * Postcondition: returns success (bool), returns linked list head 
 *                via parameter, returns endstate/error message via parameter
 * Exceptions: function halted and error message returned if bad input data
 * Notes: None
 */
bool getMetaData( char *fileName,
                  OpCodeType **opCodeDataHead, char *endStateMsg )
   {
    // initialize function/variables

        // initialize read only constant
        const char READ_ONLY_FLAG[] = "r";

        // initialize variables
        int accessResult, startCount = 0, endCount = 0;
        char dataBuffer[ MAX_STR_LEN ];
        //bool ignoreLeadingWhiteSpace = true;
        //bool stopAtNonPrintable = true;
        bool returnState = true;
        OpCodeType *newNodePtr;
        OpCodeType *localHeadPtr = NULL;
        FILE *fileAccessPtr;

    // initialize op code data pointer in case of return error
    *opCodeDataHead = NULL;

    // initialize end state message
        // function: copyString
    copyString( endStateMsg, "Metadata file upload successful" );

    // open file for reading 
        // function: open
    fileAccessPtr = fopen( fileName, READ_ONLY_FLAG );

    //fprintf( stderr, "fileName:\t%s\n", fileName );

    // check for file open failure
    if( fileAccessPtr == NULL )
       {
        // set end state message
            // function: copyString
        copyString( endStateMsg, "Metadata file access error" );

        // return file access error
        return false;
       }

    // check first line for correct leader
        // function: getStringToDelimiter, compareString
    if( ! getStringToDelimiter ( fileAccessPtr, COLON, dataBuffer ) 
                    || compareString( dataBuffer, "Start Program Meta-Data Code" ) != STR_EQ )
       {
        // close file
            // function: fclose
        fclose( fileAccessPtr );

        // set end state message
            // function: copyString
        copyString( endStateMsg, "Corrupt metadata leader line error" );

        // return corrupt descriptor error
        return false;
       }

    // allocate memory for the temporary data structure
        // function: malloc
    newNodePtr = ( OpCodeType * )malloc( sizeof( OpCodeType ) );

    // get the first op command
        // function: getOpCommand
    accessResult = getOpCommand( fileAccessPtr, newNodePtr );

    // get start and end counts for later comparison
        // function: updateStartCount, updateEndCount
    startCount = updateStartCount( startCount, newNodePtr->strArg1 );
    endCount = updateEndCount( endCount, newNodePtr->strArg1 );

    // check for failure of first complete op method
    if( accessResult != COMPLETE_OPCMD_FOUND_MSG )
       {
        // close file
            // function: fclose
        fclose( fileAccessPtr );

        // clear data from the structure list
            // function: clearMetaDataList
        *opCodeDataHead = clearMetaDataList( localHeadPtr );

        // free temporary structure memory
            // function: free
        free( newNodePtr );
            
        // set end state message
            // function: copyString
        copyString( endStateMsg, "Metadata incomplete first op command found" );

        // return result of operation
        return false;
       }

    // loop across all remaining op commands
    //    (while complete op commands are found)
    while( accessResult == COMPLETE_OPCMD_FOUND_MSG )
       {
        // add the new op commad to the linked list
            // function: addNode
        localHeadPtr = addNode( localHeadPtr, newNodePtr );

        // get a new op command
            // function: getOpCommand
        accessResult = getOpCommand( fileAccessPtr, newNodePtr );

        // update start and end counts for later comparison
            // function: updateStartCount, updateEndCount
        startCount = updateStartCount( startCount, newNodePtr->strArg1 );
        endCount = updateEndCount( endCount, newNodePtr->strArg1 );
       }
    // end loop across remaining op commands

    // after loop completion, check for last op command found
    if( accessResult == LAST_OPCMD_FOUND_MSG )
       {
        // check for start and end op code counts equal
        if( startCount == endCount )
           {
            // add the last node to the linked list
                // function: addNode
            localHeadPtr = addNode( localHeadPtr, newNodePtr );

            // set access result to no error for later operation
            accessResult = NO_ERR;

            // check last line for incorrect end descriptor
                // function: getStringToDelimiter, compareString
            if( ! getStringToDelimiter ( fileAccessPtr, PERIOD, dataBuffer ) 
                            || compareString( dataBuffer, "End Program Meta-Data Code" ) != STR_EQ )
               {
                // set access result to corrupted descriptor error
                accessResult = MD_CORRUPT_DESCRIPTOR_ERR;

                // set end state message
                    // function: copyString
                copyString( endStateMsg, "Metadata corrupted descriptor error" );
               }
           }
       }

    // otherwise, assume didn't find end
    else
       {
        // set end state message
            // function: copyString
        copyString( endStateMsg, "Corrupted metadata op code" );

        // unset return state
        returnState = false;
       }

    // check for any errors found (not no error)
    if( accessResult != NO_ERR )
       {
        // clear the op command list
            // function: clearMetaDataList
        localHeadPtr = clearMetaDataList( localHeadPtr );
       }

    // close access file
        // function: fclose
    fclose( fileAccessPtr );

    // release temporary message structure memory
        // function: free
    free( newNodePtr );

    // assign temporary local head pointer to parameter return pointer
    *opCodeDataHead = localHeadPtr;

    // return access result
    return returnState;
   }

/*
 * Function Name: getNumberArg
 * Algorithm: skips leading white space, acquires next integer from op 
 *            command input string, ending at comma or end of string 
 * Precondition: input string has some remaining string argument
 * Postcondition: in correct operation, captures next integer argument,
 *                returns index location after process finished
 * Exceptions: None
 * Notes: None
 */
int getNumberArg( int *number, char *inputStr, int index )
   {
    // initialize function/variables
    bool foundDigit = false;
    *number = 0;
    int multiplier = 1;

    // loop to skip white space
    while( inputStr[ index ] <= SPACE || inputStr[ index ] == COMMA )
       {
        // increment index
        index++;
       }

    // loop across string length
        // function: isDigit
    while( isDigit( inputStr[ index ] ) && inputStr[ index ] != NULL_CHAR )
           {
            // set digit found flag
            foundDigit = true;

            // assign digit to output
            (*number) = (*number) * multiplier + inputStr[ index ] - '0';

            // increment index and multiplier
            index++; multiplier = 10;
           }
    // end loop across string length

    // check for digit not found
    if( !foundDigit )
       {
        // set number to BAD_ARG_VAL constant
        *number = BAD_ARG_VAL;
       }

    // return current index
    return index;
   }

/*
 * Function Name: getOpCommand
 * Algorithm: acquires one op command, verifies all parts of it, 
 *            returns as parameter
 * Precondition: file is open and file cursor is at beginning of an op code
 * Postcondition: in correct operation, finds, tests, and returns op command 
 *                as parameter, and returns status as integer - either complete 
 *                op command found, or last op command found
 * Exceptions: correctly and appropriately (without program failure) responds 
 *             to and reports file access failure, incorrectly formatted op 
 *             command letter, incorrectly formatted op command name, 
 *             incorrect or out of range op command value
 * Notes: None
 */
int getOpCommand( FILE *filePtr, OpCodeType *inData )
   {
    // initialize function/variables

      // initialize local constants
      const int MAX_CMD_LENGTH = 5;
      const int MAX_ARG_STR_LENGTH = 15;

      // initialize other variables
      int accessResult, numBuffer = 0;
      char strBuffer[ STD_STR_LEN ];
      char cmdBuffer[ MAX_CMD_LENGTH ];
      char argStrBuffer[ MAX_ARG_STR_LENGTH ];
      int runningStringIndex = 0;
      bool arg2FailureFlag = false;
      bool arg3FailureFlag = false;

    // get whole op command as a string
        // function: getStringToDelimiter
    if ( getStringToDelimiter ( filePtr, SEMICOLON, strBuffer )  )
      {
       accessResult = NO_ERR;
      }
    else 
      {
       accessResult = !NO_ERR;
      }
    // check for successful access
    if( accessResult == NO_ERR )
       {
        // get three-letter command
            // function: getCommand
        runningStringIndex = getCommand( cmdBuffer, strBuffer, 
                                         runningStringIndex );

        // assign op command to node
            // function: copyString
        copyString( inData->command, cmdBuffer );
       }

    // otherwise, assume unsuccessful access
    else
       {
        // set pointer to data structure to null
        inData = NULL;

        // return op command access failure
        return OPCMD_ACCESS_ERR;
       }

    // verify op command
        // function: verifyValidCommand
    if( !verifyValidCommand( cmdBuffer ) )
       {
        // return op command error
        return CORRUPT_OPCMD_ERR;
       }

    // set all struct values that may not be initialized to defaults
    inData->pid = 0;
    inData->inOutArg[ 0 ] = NULL_CHAR;
    inData->intArg2 = 0;
    inData->intArg3 = 0;
    inData->opEndTime = 0.0;
    inData->nextNode = NULL;

    // check for device command
        // function: compareString
    if( compareString( cmdBuffer, "dev" ) == STR_EQ ) 
       {
        // get in/out argument
            // function: getStringArg
        runningStringIndex = getStringArg( argStrBuffer, strBuffer, 
                                           runningStringIndex );

        // set device in/out argument
            // function: copyString
        copyString( inData->inOutArg, argStrBuffer );

        // check correct argument
            // function: compareString
        if( compareString( argStrBuffer, "in" ) != STR_EQ 
              && compareString( argStrBuffer, "out" ) != STR_EQ )
           {
            // return argument error
            return CORRUPT_OPCMD_ARG_ERROR;
           }
       } 

    // get first string arg
        // function: getStringArg
    runningStringIndex = getStringArg( argStrBuffer, strBuffer,
                                       runningStringIndex );

    // set device in/out argument
        // function: copyString
    copyString( inData->strArg1, argStrBuffer );

    // check for legitimate first string arg
        // function: verifyFirstStringArg
    if( !verifyFirstStringArg( argStrBuffer ) )
       {
        // return argument error
        return CORRUPT_OPCMD_ARG_ERROR;
       }

    // check for last op command found
        // function: compareString
    if( compareString( inData->command, "sys" ) == STR_EQ
                       && compareString( inData->strArg1, "end" ) == STR_EQ )
       {
        // return last op command found 
        return LAST_OPCMD_FOUND_MSG;
       }

    // chec for app start seconds argument
        // function: compareString
    if( compareString( inData->command, "app" ) == STR_EQ 
                       && compareString( inData->strArg1, "start" ) == STR_EQ )
       {
        // get number argument
            // function: getNumberArg
        runningStringIndex = getNumberArg( &numBuffer, strBuffer, 
                                 runningStringIndex );

        // check for failed number access
        if( numBuffer <= BAD_ARG_VAL )
           {
            // set failure flag
            arg2FailureFlag = true;
           }

        // set first int argument to number
        inData->intArg2 = numBuffer;
       }

    // check for cpu cycle time
        // function: compareString
    else if( compareString( inData->command, "cpu" ) == STR_EQ )
       {
        // get number argument
            // function: getNumberArg
        runningStringIndex = getNumberArg( &numBuffer, strBuffer,
                                           runningStringIndex );

        // check for failed number access
        if( numBuffer <= BAD_ARG_VAL )
           {
            // set failure flag
            arg2FailureFlag = true;
           }

        // set first int argument to number
        inData->intArg2 = numBuffer;
       }

    // check for device cycle time
        // function: compareString
    else if( compareString( inData->command, "dev" ) == STR_EQ )
       {
        // get number argument
            // function: getNumberArg
        runningStringIndex = getNumberArg( &numBuffer, strBuffer,
                                           runningStringIndex );

        // check for failed number access
        if( numBuffer <= BAD_ARG_VAL )
           {
            // set failure flag
            arg2FailureFlag = true;
           }

        // set first int argument to number
        inData->intArg2 = numBuffer;
       }

    // check for memory base and offset
        // function: compareString
    else if( compareString( inData->command, "mem" ) == STR_EQ )
       {
        // get number argument for base
            // function: getNumberArg
        runningStringIndex = getNumberArg( &numBuffer, strBuffer,
                                           runningStringIndex );
            
        // check for failed number access
        if( numBuffer <= BAD_ARG_VAL )
           {
            // set failure flag
            arg2FailureFlag = true;
           }
            
        // set first int argument to number
        inData->intArg2 = numBuffer;

        // get number argument for offset
            // function: getNumberArg
        runningStringIndex = getNumberArg( &numBuffer, strBuffer,
                                           runningStringIndex );

        // check for failed number access
        if( numBuffer <= BAD_ARG_VAL )
           {
            // set failure flag
            arg3FailureFlag = true;
           }
        
        // set second int argument to number
        inData->intArg3 = numBuffer;
       }

    // check int args for upload failure
    if( arg2FailureFlag || arg3FailureFlag )
       {
        // return argument error
        return CORRUPT_OPCMD_ARG_ERROR;
       }

    // return complete op command found message
    return COMPLETE_OPCMD_FOUND_MSG;
   }

/*
 * Function Name: getStringArg
 * Algorithm: skips leading white space, acquires sub string from op command 
 *            input string ending at comma or end of string 
 * Precondition: input string has some remaining string argument
 * Postcondition: in correct operation, captures next string argument
 * Exceptions: None
 * Notes: None
 */
int getStringArg( char *strArg, char *inputStr, int index )
   {
    // function/variable initialization
    int localIndex = 0;
    
    // loop to skip white space and comma
    while( inputStr[ index ] <= SPACE || inputStr[ index ] == COMMA )
       {
        // increment index
        index++;
       }

    // loop across string length
    while( inputStr[ index ] != COMMA && inputStr[ index ] != NULL_CHAR )
       {
        // assign character from input string to buffer string
        strArg[ localIndex ] = inputStr[ index ];

        // increment index
        index++; localIndex++;

        // set next character to null character
        strArg[ localIndex ] = NULL_CHAR;
       }
    // end loop across string length

    // return current index
    return index;
   }

/*
 * Function Name: isDigit
 * Algorithm: checks for character digit, returns result
 * Precondition: test value is character
 * Postcondition: if test value is a value '0' < value < '9',
 *                returns true, otherwise returns false
 * Exceptions: None
 * Notes: None
 */
bool isDigit( char testChar )
   {
    // check for test character between characters '0' - '9'
    if( testChar >= '0' && testChar <= '9' )
       {
        return true;
       }

    // otherwise, assume character is not digit, return false
    return false; 
   }

/*
 * Function Name: updateStartCount
 * Algorithm: updates number of "start" op commands found in file
 * Precondition: count >= 0, op string has "start" or other op name
 * Postcondition: if op string has "start", input count + 1 is returned;
 *                otherwise, input count is returned unchanged
 * Exceptions: None
 * Notes: None
 */
int updateStartCount( int count, char *opString )
   {
    // check for "start" in op string
        // function: compareString
    if( compareString( opString, "start" ) == STR_EQ )
       {
        // return incremented start count
        return count + 1;
       }

    // return unchanged start count
    return count;
   }

/*
 * Function Name: updateEndCount
 * Algorithm: updates number of "end" op commands found in file
 * Precondition: count >= 0, op string has "end" or other op name
 * Postcondition: if op string has "end", input count + 1 is returned;
 *                otherwise, input count is returned unchanged
 * Exceptions: None
 * Notes: None
 */
int updateEndCount( int count, char *opString )
   {
    // check for "end" in op string
        // function: compareString
    if( compareString( opString, "end" ) == STR_EQ )
       {
        // return incremented end count
        return count + 1;
       }

    // return unchanged end count
    return count;
   }

/*
 * Function Name: verifyFirstStringArg
 * Algorithm: check string argument for one of the allowed string arguments
 * Precondition: input string is provided
 * Postcondition: in correct operation, reports true if given string is in 
 *                argument list, false if not
 * Exceptions: None
 * Notes: None
 */
bool verifyFirstStringArg( char *strArg )
   {
    // check for all possible string arg 1 possibilities
        // function: compareString
    if( compareString( strArg, "access" ) == STR_EQ
        || compareString( strArg, "allocate" ) == STR_EQ
        || compareString( strArg, "end" ) == STR_EQ
        || compareString( strArg, "ethernet" ) == STR_EQ
        || compareString( strArg, "hard drive" ) == STR_EQ
        || compareString( strArg, "keyboard" ) == STR_EQ
        || compareString( strArg, "monitor" ) == STR_EQ
        || compareString( strArg, "printer" ) == STR_EQ
        || compareString( strArg, "process" ) == STR_EQ
        || compareString( strArg, "serial" ) == STR_EQ
        || compareString( strArg, "sound signal" ) == STR_EQ
        || compareString( strArg, "start" ) == STR_EQ
        || compareString( strArg, "usb" ) == STR_EQ
        || compareString( strArg, "video signal" ) == STR_EQ )
       {
        return true;
       }

    return false;
   }

/*
 * Function Name: verifyValidCommand
 * Algorithm: check string argument for one of the allowed commands
 * Precondition: input string is provided
 * Postcondition: in correct operation, reports if given string is a command, 
 *                false if not
 * Exceptions: None
 * Notes: None
 */
bool verifyValidCommand( char *testCmd )
   {
    // check for five string command arguments
    if( compareString( testCmd, "sys" ) == STR_EQ 
        || compareString( testCmd, "app" ) == STR_EQ
        || compareString( testCmd, "cpu" ) == STR_EQ
        || compareString( testCmd, "mem" ) == STR_EQ
        || compareString( testCmd, "dev" ) == STR_EQ )
       {
        return true;
       }

    return false;
   }