Search code examples
cstringsymbolsisalpha

Is there an exception for ispunct() in C?


I want to write a code that will check the correctness of MAC address. Input should look for example like this D7:6E:F4:30:17:2B. Im thinking about using functions isdigit() and isupper(). Dont know how to make user avalaible to write " : " symbol and stop him from writting other symbols.

if(user input is 13:4F:60:AC:7O:DE)
    ... all good
if(user input is 14:a]:!o:0L)
    ... wrong input, retry

EDIT According to @Woodrow Barlow answer i've wrote that code:

int mac_address() 
    {   
        int is_valid = 1;
        printf("MAC ADDRESS:");
        fgets(mac_addr, sizeof(mac_addr), stdin); 
            if (mac_addr[sizeof(mac_addr) - 1] != '\0')
            {
                is_valid = 0;
            }
            else if (ether_aton(mac_addr) == NULL)
            {
                is_valid = 0;
                // input isn't recognizable as a MAC address
            }
            if (is_valid == 1)
            {
                system("clear");
                printf("valid!\n");
                printf("%s\n", mac_addr);
                return license_menu();
            }
            else {
                printf("invalid!\n");
                fflush(stdin);
                return 1;
            }
    }

Solution

  • The best way to parse a MAC address or check its validity is to use ether_aton. MAC addresses can come in many formats, and ether_aton can be relied on to parse them.

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <netinet/ether.h>
    
    int main(int argc, const char *argv[])
    {
        char mac_addr[64];
    
        while (true)
        {
            fgets(mac_addr, sizeof(mac_addr), stdin);
            if (mac_addr[sizeof(mac_addr) - 1] != '\0')
            {
                // input was too long for the buffer
                printf("invalid!\n");
            }
            else if (ether_aton(mac_addr) == NULL)
            {
                // input isn't recognizable as a MAC address
                printf("invalid!\n");
            }
            else
            {
                break;
            }
        }
    
        printf("valid!\n");
        printf("%s\n", mac_addr);
        return 0;
    }
    

    It sounds like you're checking one character at a time, that you want to reject an invalid character immediately without waiting for the full string of input, and that you specifically want to reject MAC addresses that have lowercase letters or that use separators other than a colon. Is that accurate? I will assume you have your own reasons for doing so.

    The ispunct function is a red herring here. There is no reason to check whether a given character is a punctuation character; what you really want to know is whether it's a colon. specifically. you can compare them directly.

    #include <stdio.h>
    #include <stdbool.h>
    #include <string.h>
    #include <unistd.h>
    #include <termios.h>
    
    bool is_valid(char ch, int i)
    {
        if ((i + 1) % 3 == 0)
        {
            return ch == ':';
        }
        else if (ch >= '0' && ch <= '9')
        {
            return true;
        }
        else if (ch >= 'A' && ch <= 'F')
        {
            return true;
        }
    
        return false;
    }
    
    int main(int argc, const char *argv[])
    {
        struct termios old_tio, new_tio;
        const int max_len = strlen("00:00:00:00:00:00");
        char mac_addr[max_len + 1];
        char ch = '\0';
        int i = 0;
        int ret = 0;
    
        /* need to modify the terminal's underlying settings, because
         * by default STDIN is buffered to support backspace, etc.
         * by switching to non-buffered input, you lose a lot of basic
         * functionality like backspace.
         * that's why it's usually recommended to just read in the entire
         * line of text and then check if it's valid at the end.
         */
        tcgetattr(STDIN_FILENO, &old_tio);
        new_tio = old_tio;
        new_tio.c_lflag &=(~ICANON);
        tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
    
        for (i = 0; i < max_len; i++)
        {
            ch = getchar();
            if (!is_valid(ch, i))
            {
                printf("\ninvalid!\n");
                ret = 1;
                goto exit;
            }
            mac_addr[i] = ch;
        }
        mac_addr[max_len] = '\0';
    
        printf("\nvalid!\n");
        printf("%s\n", mac_addr);
    
    exit:
        /* this is important; need to reset the terminal
         * settings to their previous value before terminating.
         */
        tcsetattr(STDIN_FILENO,TCSANOW,&old_tio);
        return ret;
    }