0

I understand the general idea of C and how making a log file would go. Reading/writing to a file and such.

My concern is the following format that is desired:

[![enter image description here][1]][1]

I've gotten a good chunk done now but am concerned with how to append to my log file after the first record. I increment the file's record count (in the top 2 bytes) and write the first record after it. How would I then setup to add the 2nd/3rd/etc records to showup after each other?

//confirm a file exists in the directory
bool fileExists(const char* file)
{
    struct stat buf;
    return (stat(file, &buf) == 0);
}

int rightBitShift(int val, int space)
{
    return ((val >> space) & 0xFF);
}

int leftBitShift(int val, int space)
{
    return (val << space);
}

int determineRecordCount(char * logName)
{
    unsigned char record[2];
    FILE *fp = fopen(logName, "rb");
    fread(record, sizeof(record), 1, fp); 

    //display the record number
    int recordNum = (record[0] << 8) | record[1];
    recordNum = recordNum +1;

    return (recordNum);
}


void createRecord(int argc, char **argv)
{
    int recordNum;
    int aux = 0;
    int dst;
    char* logName;
    char message[30];
    memset(message,' ',30);

    //check argument count and validation 
    if (argc == 7 && strcmp("-a", argv[2]) ==0 && strcmp("-f", argv[3]) ==0 && strcmp("-t", argv[5]) ==0)
    {
        //aux flag on
        aux = 1;
        logName = argv[4];
        strncpy(message, argv[6],strlen(argv[6]));
    }
    else if (argc == 6 && strcmp("-f", argv[2]) ==0 && strcmp("-t", argv[4]) ==0)
    {
        logName = argv[3];
        strncpy(message, argv[5],strlen(argv[5]));
    }
    else
    {
        printf("Invalid Arguments\n");
        exit(0);
    }

    //check if log exists to get latest recordNum
    if (fileExists(logName))
    {
        recordNum = determineRecordCount(logName);
        printf("%i\n",recordNum);
    }
    else
    {
        printf("Logfile %s not found\n", logName);
        recordNum = 1;
    }


    //Begin creating record
    unsigned char record[40]; /* One record takes up 40 bytes of space */
    memset(record, 0, sizeof(record));

    //recordCount---------------------------------------------------------------------
    record[0] = rightBitShift (recordNum, 8); /* Upper byte of sequence number */
    record[1] = rightBitShift (recordNum, 0); /* Lower byte of sequence number */

    //get aux/dst flags---------------------------------------------------------------
    //get date and time
    time_t timeStamp = time(NULL);
    struct tm *date = localtime( &timeStamp );
    if (date->tm_isdst)
        dst = 1;
    record[2] |= aux << 7;  //set 7th bit
    record[2] |= dst << 6;  //set 6th

    //timeStamp-----------------------------------------------------------------------
    record[3] |= rightBitShift(timeStamp, 24);//high byte
    record[4] |= rightBitShift(timeStamp, 16);
    record[5] |= rightBitShift(timeStamp, 8);
    record[6] |= rightBitShift(timeStamp, 0); //low byte

    //leave bytes 7-8, set to 0 -----------------------------------------
    record[7] = 0;
    record[8] = 0;

    //store message--------------------------------------------
    strncpy(&record[9], message, strlen(message));


    //write record to log-----------------------------------------------------------------
    FILE *fp = fopen(logName, "w+");

     unsigned char recordCount[4];
    recordCount[0] = rightBitShift (recordNum, 8); /* Upper byte of sequence number */
    recordCount[1] = rightBitShift (recordNum, 0); /* Lower byte of sequence number */
    recordCount[2] = 0;
    recordCount[3] = 0;

    fwrite(recordCount, sizeof(recordCount), 1, fp);

    fwrite(record, sizeof(record), 1, fp);
    fclose(fp); 

    printf("Record saved successfully\n");
}
Jack Tom
  • 19
  • 2
  • 1
    Hi, and welcome to StackOverflow. We prefer text to images. If you're going to use an image, please embed it as I have done. – Schwern Mar 26 '17 at 20:47
  • The sum is probably the addition of all octets (as unsigned values) ignoring the carry / overflow, or its complement. [for IP the sum is summed over 16 bytes entities] – wildplasser Mar 26 '17 at 21:28
  • Could you link to the full spec? There's some details missing. – Schwern Mar 26 '17 at 22:04
  • 1
    There's an error in your format. "Spare (3 bytes)" but it only has two bytes, 7 and 8. – Schwern Mar 26 '17 at 22:39
  • I see you have [cross-posted](https://www.reddit.com/r/C_Programming/comments/61nje8/offset_and_bites/) this from reddit. Please tell us that you did this so no work is duplicated. – fuz Mar 27 '17 at 13:17

2 Answers2

1

NOTE: I've never had to do this before in C, take it with a grain of salt.

This is a very specific binary formatting where each bit is precisely accounted for. It's using the Least-Significant-Bit numbering scheme (LSB 0) where the bits are numbered from 7 to 0.

Specifying that the "upper byte" comes first means this format is big-endian. The most significant bits come first. This is like how we write our numbers, four thousand, three hundred, and twenty one is 4321. 1234 would be little-endian. For example, the Number Of Records and Sequence are both 16 bit big-endian numbers.

Finally, the checksum is a number calculated from the rest of the record to verify there were no mistakes in transmission. The spec defines how to make the checksum.

Your job is to precisely reproduce this format, probably using the fixed-sized types found in stdint.h or unsigned char. For example, the sequence would be a uint16_t or unsigned char[2].

The function to produce a record might have a signature like this:

unsigned char *make_record( const char *message, bool aux );

The user only has to supply you with the message and the aux flag. The rest you can be figured out by the function. You might decide to let them pass in the timestamp and sequence. Point is, the function needs to be passed just the data, it takes care of the formatting.

This byte-ordering means you can't just write out integers, they might be the wrong size or the wrong byte order. That means any multi-byte integers must be serialized before you can write them to the record. This answer covers ways to do that and I'll be using the ones from this answer because they proved a bit more convenient.

#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

unsigned char *make_record( const char *message, bool aux ) {
    // Allocate and zero memory for the buffer.
    // Zeroing means no risk of accidentally sending garbage.
    unsigned char *buffer = calloc( 40, sizeof(unsigned char) );

    // As we add to the buffer, pos will track the next byte to be written.
    unsigned char *pos = buffer;

    // I decided not make the user responsible for
    // the sequence number. YMMV.
    static uint16_t sequence = 1;    
    pos = serialize_uint16( pos, sequence );

    // Get the timestamp and DST.
    time_t timestamp = time(NULL);
    struct tm *date = localtime( &timestamp );

    // 2nd row is all flags and a bunch of 0s. Start with them all off.
    uint8_t flags = 0;
    if( aux ) {
        // Flip the 7th bit on.
        flags |= 0x80;
    }
    if( date->tm_isdst ) {
        // Flip the 6th bit on.
        flags |= 0x40;
    }

    // That an 8 bit integer has no endianness, this is to ensure
    // pos is consistently incremented.
    pos = serialize_uint8(pos, flags);

    // I don't know what their timestamp format is.
    // This is just a guess. It's probably wrong.
    pos = serialize_uint32(pos, (uint32_t)timestamp);

    // "Spare" is all zeros.
    // The spec says this is 3 bytes, but only gives it bytes
    // 7 and 8. I'm going with 2 bytes.
    pos = serialize_uint16(pos, 0);

    // Copy the message in, 30 bytes.
    // strncpy() does not guarantee the message will be null
    // terminated. This is probably fine as the field is fixed width.
    // More info about the format would be necessary to know for sure.
    strncpy( pos, message, 30 );
    pos += 30;

    // Checksum the first 39 bytes.
    // Sorry, I don't know how to do 1's compliment sums.
    pos = serialize_uint8( pos, record_checksum( buffer, 39 ) );

    // pos has moved around, but buffer remains at the start
    return buffer;
}

int main() {
    unsigned char *record = make_record("Basset hounds got long ears", true);
    fwrite(record, sizeof(unsigned char), 40, stdout);
}

At this point my expertise is exhausted, I've never had to do this before. I'd appreciate folks fixing up the little mistakes in edits and suggesting better ways to do it in the comments, like what to do with the timestamp. And maybe someone else can cover how to do 1's compliment checksums in another answer.

Community
  • 1
  • 1
Schwern
  • 153,029
  • 25
  • 195
  • 336
0

As a byte is composed by 8 bits (from 0 to 7) you can use bitwise operations to modify them as asked in your specifications. Take a look for general information (https://en.wikipedia.org/wiki/Bitwise_operations_in_C). As a preview, you can use >> or << operators to determine which bit to modify, and use logical operators | and & to set it's values.

DvTr
  • 347
  • 1
  • 5