coconut crab
coconut crab

Reputation: 11

Storing mac addresses and device names listed in a text file into a struct array using C

I am trying to take a list of MAC Addresses and Device names from a text file and store them in an array called list;

struct list {
    char ch; 
    char a[2], b[2], c[2], d[2], e[2], f[2], g[2], alias[32]; //chars a-g are supposed to be a maximum of two characters long to store the different segments of the mac addresses, and the alias stores the device name up to 32 characters. 
};

The main function here, as of right now is supposed to open the file "Inet.txt" and read each character individually using "char cur = fgetc." The function then assigns the different parts of the MAC address to its corresponding position in chars a-g of the list struct, and the alias char if the function goes more than 2 chars without reaching a ":" or a " ". The length of the current char is represented by the variable k, which increases every time the program detects a letter or a number, and is reset to -1 every time variable 'cur' is assigned to something. There is also an array "struct list *head[32]; " which stores each line separately, the line number being identified by the variable "int i", which increases by one every time "cur == '\n'" starting at "int = 0." The main function is as follows;

int main() 
{

    FILE *fp;
    char cur, temp[32], temp2[32], p;
    struct list *head[32]; 
    head[0]=(struct list*)malloc(sizeof(struct list));
    int num = 0, d, data, devices, i = 0, j = -1, k = -1, l = 0; 
    char arr[100][2];

    int count = 0; 
    //head = current = NULL;
    fp = fopen("Inet.txt", "r");

    if(fp==NULL)
    {
        printf("Error opening file."); 
    }

    while((cur = fgetc(fp)) != EOF)
    {
       //stringchecker(cur)!=0
        if((cur >= 48 && cur <= 57)||(cur >= 97 && cur <= 122)||(cur >= 65 && cur <= 90))
        {
           k++; //counter for temp array size
           
           if(cur >= 97 && cur <= 122)
           {
               temp[k] = cur-32; 
           }
           else
           {
               temp[k] = cur;
           }
           if(k>1)
           {
            strncpy(temp2, temp, k+1);
            temp2[k+1] = '\0';
            aloc(&head[i],temp2,7);
            // k = -1; 
           }
        }
        else if(cur == ':')
        {
            if(count == 0)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 1)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 2)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 3)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 4)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 5)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            count++;
            k = -1; 
        }
        else if(cur == ' ')
        {
            strncpy(temp2, temp, 2);
            temp2[2] = '\0';
            aloc(&head[i],temp2,6);
            k = -1; 
        }
        else if(cur == '\n')
        {
            printf("\n%s:%s:%s:%s:%s%s\nALIAS: %s", (*head[i]).a,(*head[i]).b,(*head[i]).c,(*head[i]).d,(*head[i]).e,(*head[i]).f,(*head[i]).alias);
            exit(0);
            devices++;
            data++;  
            count = 0;
            num = -1; 
            i++;  
            j = -1; 
            k = -1; 

           
           head[i]=(struct list*)malloc(sizeof(struct list));
           //exit(0); 
        }       
    }
    fclose(fp);
    return 0;
}

The "aloc()" function assigns the current char up to 16 characters to a-g or alias depending on the value of the variable count, which is a parameter of this function. The aloc() function is as follows;

void aloc(struct list **head, char ch[16], int count) //assigns ch value to specific variable of the current head based on the value of count 1-7
{
     if(count == 0)
     {
        strncpy((*head)->a,ch, 2);
     }
     else if(count == 1)
     {
        strncpy((*head)->b,ch, 2);
     }
     else if(count == 2)
     {
        strncpy((*head)->c,ch, 2);
     }
     else if(count == 3)
     {
        strncpy((*head)->d,ch, 2);
     }
     else if(count == 4)
     {
        strncpy((*head)->e,ch, 2);
     }
     else if(count == 5)
     {
         strncpy((*head)->f,ch, 2);
     }
     else if(count == 6)
     {
        strncpy((*head)->g,ch, 2);
     }
     else if(count == 7)
     {
        strncpy((*head)->alias,ch, 16);
     }
}

The input text file "Inet.txt" is as follows;

A0:FB:C5:44:b8:45 PLATTE
58:24:29:0f:c8:ee JET
F1:C0:11:16:53:1F Wabash
A0:FB:C5:32:15:10 GREEN
33:68:29:a1:b2:3c Charlie
58:24:29:0A:0B:C0 BAKER
GG:01:X0:99:1A:45 FOXTROT

The main problem I am having with this code is the variables a-g are not being assigned correctly. When I run the program to only read the first line, I get the following output:

A0FBC544B8:FBC544B8:C544B8:44B8:B8
ALIAS: PLATTE%  

When the output should be:

A0:FB:C5:44:B8
ALIAS: PLATTE

I am not sure which line is causing the entire mac address to be assigned to char a of the current list. I will post the code as I have it in its entirety here to avoid confusion.

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

struct list {
    char ch; 
    char a[2], b[2], c[2], d[2], e[2], f[2], g[2], alias[32]; 
};
void aloc(struct list **head, char ch[16], int count)
{
     if(count == 0)
     {
        strncpy((*head)->a,ch, 2);
     }
     else if(count == 1)
     {
        strncpy((*head)->b,ch, 2);
     }
     else if(count == 2)
     {
        strncpy((*head)->c,ch, 2);
     }
     else if(count == 3)
     {
        strncpy((*head)->d,ch, 2);
     }
     else if(count == 4)
     {
        strncpy((*head)->e,ch, 2);
     }
     else if(count == 5)
     {
         strncpy((*head)->f,ch, 2);
     }
     else if(count == 6)
     {
        strncpy((*head)->g,ch, 2);
     }
     else if(count == 7)
     {
        strncpy((*head)->alias,ch, 16);
     }
    

}
int main() 
{

    FILE *fp;
    char cur, temp[32], temp2[32], p;
    struct list *head[32]; 
    head[0]=(struct list*)malloc(sizeof(struct list));
    int num = 0, d, data, devices, i = 0, j = -1, k = -1, l = 0; 
    char arr[100][2];

    int count = 0; 
    //head = current = NULL;
    fp = fopen("Inet.txt", "r");

    if(fp==NULL)
    {
        printf("Error opening file."); 
    }

    while((cur = fgetc(fp)) != EOF)
    {
       //stringchecker(cur)!=0
        if((cur >= 48 && cur <= 57)||(cur >= 97 && cur <= 122)||(cur >= 65 && cur <= 90))
        {
           k++; //counter for temp array size
           
           if(cur >= 97 && cur <= 122)
           {
               temp[k] = cur-32; 
           }
           else
           {
               temp[k] = cur;
           }
           if(k>1)
           {
            strncpy(temp2, temp, k+1);
            temp2[k+1] = '\0';
            aloc(&head[i],temp2,7);
            // k = -1; 
           }
        }
        else if(cur == ':')
        {
            if(count == 0)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 1)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 2)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 3)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 4)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            else if(count == 5)
            {
                strncpy(temp2, temp, 2);
                temp2[2] = '\0';
                aloc(&head[i],temp2,count);
            }
            count++;
            k = -1; 
        }
        else if(cur == ' ')
        {
            strncpy(temp2, temp, 2);
            temp2[2] = '\0';
            aloc(&head[i],temp2,6);
            k = -1; 
        }
        else if(cur == '\n')
        {
            printf("\n%s:%s:%s:%s:%s%s\nALIAS: %s", (*head[i]).a,(*head[i]).b,(*head[i]).c,(*head[i]).d,(*head[i]).e,(*head[i]).f,(*head[i]).alias);
            exit(0);
            devices++;
            data++;  
            count = 0;
            num = -1; 
            i++;  
            j = -1; 
            k = -1; 

           
           head[i]=(struct list*)malloc(sizeof(struct list));
           //exit(0); 
        }       
    }
    fclose(fp);
    return 0;
}

I initially tried writing this program using linked lists, but I thought it would be easier to keep track of an array of list structs for use later in my program. However I keep getting the same problem with my output. Any help is appreciated.

Upvotes: 1

Views: 121

Answers (1)

Oka
Oka

Reputation: 26385

If you remove exit(0); from the block here

else if(cur == '\n')
{
    printf(/* ... */);
    exit(0);
    devices++;
    data++;  
    count = 0;
    /* ... */

then this program appears to work1.

I say "appears" because this program invokes Undefined Behaviour by printing non null-terminated buffers with the printf specifier %s.

You need to either specify a precision, being the maximum number of bytes to print, with each %s specifier. For example:

#include <stdio.h>

int main(void)
{
    char buf[2] = "AB"; /* the null byte is not stored */

    printf("%2s\n", buf);
}

Or, you need to ensure your buffers are large enough to store a desired string length plus the null-terminating byte. If you want to store a string of length 2, your buffer must be at least of size 3.

#include <stdio.h>

int main(void)
{
    char buf[3] = "AB"; /* the null byte IS stored */

    printf("%s\n", buf);
}

Note that strncpy is notoriously hard to use, as it does not null-terminate the buffer if the length of the source string is greater than or equal to the size provided.


1. You must also change char cur to int cur. On platforms when char is an unsigned type, you will not be able to reliably test against the negative int value of EOF. fgetc returns an int for this reason.

As pointed out in the comments, avoid magic numbers and instead use the functions found in <ctype.h>.


If your file contents are predictably formatted, you can just use fgets + sscanf to read each line. For example:

#include <stdio.h>
#include <stdlib.h>

#define MAX_ADDRS 256

struct address {
    char a[3];
    char b[3];
    char c[3];
    char d[3];
    char e[3];
    char f[3];
    char alias[32];
};

size_t read_macs(struct address *addrs, size_t limit, FILE *f)
{
    char buffer[512];
    size_t n = 0;

    while (n < limit && fgets(buffer, sizeof buffer, f)) {
        int cv = sscanf(buffer, "%2s:%2s:%2s:%2s:%2s:%2s%31s",
                addrs[n].a, addrs[n].b, addrs[n].c,
                addrs[n].d, addrs[n].e, addrs[n].f,
                addrs[n].alias);

        if (7 == cv)
            n++;
    }

    return n;
}

int main(int argc, char **argv)
{
    if (argc < 2) {
        fprintf(stderr, "usage: %s FILENAME\n", *argv);
        return EXIT_FAILURE;
    }

    FILE *file = fopen(argv[1], "r");

    if (!file) {
        perror(argv[1]);
        return EXIT_FAILURE;
    }

    struct address store[MAX_ADDRS];
    size_t length = read_macs(store, MAX_ADDRS, file);

    fclose(file);

    for (size_t i = 0; i < length; i++)
        printf("%s (%s:....:%s)\n",
                store[i].alias, store[i].a, store[i].f);
}
$ ./a.out Inet.txt 
PLATTE (A0:....:45)
JET (58:....:ee)
Wabash (F1:....:1F)
GREEN (A0:....:10)
Charlie (33:....:3c)
BAKER (58:....:C0)
FOXTROT (GG:....:45)

Upvotes: 2

Related Questions